ITKeyword,专注技术干货聚合推荐

注册 | 登录

解决How to use Parcel in Android?

itPublisher 分享于

2021腾讯云限时秒杀,爆款1核2G云服务器298元/3年!(领取2860元代金券),
地址https://cloud.tencent.com/act/cps/redirect?redirect=1062

2021阿里云最低价产品入口+领取代金券(老用户3折起),
入口地址https://www.aliyun.com/minisite/goods

推荐:Android Parcel

android 中Parcel 的使用,他是一个存储基本数据类型和引用数据类型的容器,在andorid 中通过IBinder来绑定数据在进程间传递数据。 Parcel parcel = Parcel.obta

I'm trying to use Parcel to write and then read back a Parcelable. For some reason, when I read the object back from the file, it's coming back as null.

public void testFoo() {
    final Foo orig = new Foo("blah blah");

    // Wrote orig to a parcel and then byte array
    final Parcel p1 = Parcel.obtain();
    p1.writeValue(orig);
    final byte[] bytes = p1.marshall();


    // Check to make sure that the byte array seems to contain a Parcelable
    assertEquals(4, bytes[0]); // Parcel.VAL_PARCELABLE


    // Unmarshall a Foo from that byte array
    final Parcel p2 = Parcel.obtain();
    p2.unmarshall(bytes, 0, bytes.length);
    final Foo result = (Foo) p2.readValue(Foo.class.getClassLoader());


    assertNotNull(result); // FAIL
    assertEquals( orig.str, result.str );
}


protected static class Foo implements Parcelable {
    protected static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
        public Foo createFromParcel(Parcel source) {
            final Foo f = new Foo();
            f.str = (String) source.readValue(Foo.class.getClassLoader());
            return f;
        }

        public Foo[] newArray(int size) {
            throw new UnsupportedOperationException();
        }

    };


    public String str;

    public Foo() {
    }

    public Foo( String s ) {
        str = s;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int ignored) {
        dest.writeValue(str);
    }


}

What am I missing?

UPDATE: To simplify the test I've removed the reading and writing of files in my original example.

android parcel
|
  this question
edited Nov 5 '09 at 2:41 asked Oct 26 '09 at 18:53 emmby 55.9k 50 147 208

 | 

5 Answers
5

解决方法

Ah, I finally found the problem. There were two in fact.

  1. CREATOR must be public, not protected. But more importantly,
  2. You must call setDataPosition(0) after unmarshalling your data.

Here is the revised, working code:

public void testFoo() {
    final Foo orig = new Foo("blah blah");
    final Parcel p1 = Parcel.obtain();
    final Parcel p2 = Parcel.obtain();
    final byte[] bytes;
    final Foo result;

    try {
        p1.writeValue(orig);
        bytes = p1.marshall();

        // Check to make sure that the byte stream seems to contain a Parcelable
        assertEquals(4, bytes[0]); // Parcel.VAL_PARCELABLE

        p2.unmarshall(bytes, 0, bytes.length);
        p2.setDataPosition(0);
        result = (Foo) p2.readValue(Foo.class.getClassLoader());

    } finally {
        p1.recycle();
        p2.recycle();
    }


    assertNotNull(result);
    assertEquals( orig.str, result.str );

}

protected static class Foo implements Parcelable {
    public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
        public Foo createFromParcel(Parcel source) {
            final Foo f = new Foo();
            f.str = (String) source.readValue(Foo.class.getClassLoader());
            return f;
        }

        public Foo[] newArray(int size) {
            throw new UnsupportedOperationException();
        }

    };


    public String str;

    public Foo() {
    }

    public Foo( String s ) {
        str = s;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int ignored) {
        dest.writeValue(str);
    }


}

|
  this answer
answered Nov 5 '09 at 2:58 emmby 55.9k 50 147 208 2   you just saved my day. THANKS. –  Matthias Aug 26 '10 at 16:43      Just don't try this with Bitmaps inside your Parcel(able) :( –  Bojan Radivojevic Bomber Feb 8 '13 at 18:08      setDataPosition(0) saved me! thanks ;) –  andrea.rinaldi.spot Jun 22 '15 at 10:12      Cool. thanks , I have more coverage now –  Akshat Jan 23 at 20:59

 | 

Beware! Dont use Parcel for serialization to a file

Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.

from http://developer.android.com/reference/android/os/Parcel.html


|
  this answer
edited Aug 24 '11 at 7:50 b_yng 9,985 4 24 33 answered Jun 8 '10 at 15:50 Carl D'Halluin 697 7 13 13   How is that helpful? –  skaffman Nov 25 '10 at 12:16 1   Can you suggest an alternative serialization mechanism? –  aaronsnoswell Jun 20 '12 at 1:00 2   @aaronsnoswell I recommend using Kryo, or implementing the Java Externalizable interface (if your object isn't complex) –  nobre Oct 8 '12 at 13:23 1   @DShaw you shouldnt use Serializable for this; it will cause tons of trouble when upgrading java version & reading old serialized data, or when upgrading your software –  Carl D'Halluin Dec 17 '13 at 22:30

 | 

I find that Parcelable is most often used in Android within data Bundles, but more specifically within a Handler that is sending and receiving messages. As an example, you might have an AsyncTask or a Runnable that needs to run in the background but post resulting data to the Main thread or Activity.

Here's a simple example. If I have a Runnable that looks like this:

package com.example;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.example.data.ProductInfo;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.squareup.okhttp.OkHttpClient;

public class AsyncRunnableExample extends Thread {
    public static final String KEY = "AsyncRunnableExample_MSG_KEY";

    private static final String TAG = AsyncRunnableExample.class.getSimpleName();
    private static final TypeToken<ProductInfo> PRODUCTINFO =
              new TypeToken<ProductInfo>() {
              };
    private static final Gson GSON = new Gson();

    private String productCode;
    OkHttpClient client;
    Handler handler;

    public AsyncRunnableExample(Handler handler, String productCode)
    {
        this.handler = handler;
        this.productCode = productCode;
        client = new OkHttpClient();
    }

    @Override
    public void run() {
        String url = "http://someserver/api/" + productCode;

        try
        {
            HttpURLConnection connection = client.open(new URL(url));
            InputStream is = connection.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);

            // Deserialize HTTP response to concrete type.
            ProductInfo info = GSON.fromJson(isr, PRODUCTINFO.getType());

            Message msg = new Message();
            Bundle b = new Bundle();
            b.putParcelable(KEY, info);
            msg.setData(b);
            handler.sendMessage(msg);

        }
        catch (Exception err)
        {
            Log.e(TAG, err.toString());
        }

    }
}

As you can see, this runnable takes a Handler in its constructor. This is called from some Activity like this:

推荐:Android - Parcel & Parcelable

对于Parcel的理解: 在Android系统中,定位为针对内存受限的设备,因此对性能要求更高,另外系统中采用了新的IPC(进程间通信)机制,必然要求使用性能更出色的对

static class MyInnerHandler extends Handler{
        WeakReference<MainActivity> mActivity;

        MyInnerHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity theActivity = mActivity.get();
            ProductInfo info = (ProductInfo) msg.getData().getParcelable(AsyncRunnableExample.KEY);

            // use the data from the Parcelable 'ProductInfo' class here

            }
        }
    }
    private MyInnerHandler myHandler = new MyInnerHandler(this);

    @Override
    public void onClick(View v) {
        AsyncRunnableExample thread = new AsyncRunnableExample(myHandler, barcode.getText().toString());
        thread.start();
    }

Now, all that is left is the heart of this question, how you define a class as Parcelable. I've chosen a fairly complex class to show because there are some things you would not see with a simple one. Here is the ProductInfo class, which Parcels and unParcels cleanly:

public class ProductInfo implements Parcelable {

    private String brand;
    private Long id;
    private String name;
    private String description;
    private String slug;
    private String layout; 
    private String large_image_url;
    private String render_image_url;
    private String small_image_url;
    private Double price;
    private String public_url;
    private ArrayList<ImageGroup> images;
    private ArrayList<ProductInfo> related;
    private Double saleprice;
    private String sizes;
    private String colours;
    private String header;
    private String footer;
    private Long productcode;

    // getters and setters omitted here

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(id);
        dest.writeString(name);
        dest.writeString(description);
        dest.writeString(slug);
        dest.writeString(layout);
        dest.writeString(large_image_url);
        dest.writeString(render_image_url);
        dest.writeString(small_image_url);
        dest.writeDouble(price);
        dest.writeString(public_url);
        dest.writeParcelableArray((ImageGroup[])images.toArray(), flags);
        dest.writeParcelableArray((ProductInfo[])related.toArray(), flags);
        dest.writeDouble(saleprice);
        dest.writeString(sizes);
        dest.writeString(colours);
        dest.writeString(header);
        dest.writeString(footer);
        dest.writeLong(productcode);
    }

    public ProductInfo(Parcel in)
    {
        id = in.readLong();
        name = in.readString();
        description = in.readString();
        slug = in.readString();
        layout = in.readString();
        large_image_url = in.readString();
        render_image_url = in.readString();
        small_image_url = in.readString();
        price = in.readDouble();
        public_url = in.readString();
        images = in.readArrayList(ImageGroup.class.getClassLoader());
        related = in.readArrayList(ProductInfo.class.getClassLoader());
        saleprice = in.readDouble();
        sizes = in.readString();
        colours = in.readString();
        header = in.readString();
        footer = in.readString();
        productcode = in.readLong();
    }

    public static final Parcelable.Creator<ProductInfo> CREATOR = new Parcelable.Creator<ProductInfo>() {
        public ProductInfo createFromParcel(Parcel in) {
            return new ProductInfo(in); 
        }

        public ProductInfo[] newArray(int size) {
            return new ProductInfo[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }
}

The CREATOR is critical, as is the resulting constructor taking a Parcel. I included the more complex data types so you could see how to Parcel and unParcel Arrays of Parcelable objects. This is a common thing when using Gson to convert JSON into objects with children as in this example.


|
  this answer
answered Nov 19 '13 at 16:58 David Shaw 2,850 1 8 26

 | 

To get a better understanding of the Parcel concept Try the below Link

http://prasanta-paul.blogspot.com/2010/06/android-parcelable-example.html

hope this helps :)


|
  this answer
answered Feb 23 '11 at 22:48 kAnNaN 1,903 2 20 34

 | 

I too had similar problem. only the following snippet from emmby and this helped me out.

    public static final Parcelable.Creator<Foo> CREATOR = new Parcelable.Creator<Foo>() {
        public Foo createFromParcel(Parcel source) {
            final Foo f = new Foo();
            f.str = (String) source.readValue(Foo.class.getClassLoader());
            return f;
        }

        public Foo[] newArray(int size) {
            throw new UnsupportedOperationException();
        }

It should be kept in each of the class that implements Parcelable


|
  this answer
answered Dec 11 '15 at 9:46 A_rmas 172 2 14

 | 

推荐:Android Parcel机制

一.先从Serialize说起        我们都知道JAVA中的Serialize机制,译成串行化、序列化……,其作用是能将数据对象存入字节流当中,在需要时重新生成对象。主要应


相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。