如何在Android中使用Parcel?

我正在尝试使用Parcel写入并读取Parcelable 。 出于某种原因,当我从文件中读取对象时,它将返回为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); } } 

我错过了什么?

更新:为了简化testing,我已经删除了我原来的例子中的文件的读写。

啊,我终于find了问题。 事实上有两个。

  1. 创作者必须是公开的,不受保护。 但更重要的是,
  2. 您必须在解组数据后调用setDataPosition(0)

这里是修改后的工作代码:

 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); } } 

谨防! 不要使用Parcel序列化到文件

包裹不是一个通用的序列化机制。 该类(以及用于将任意对象放入Parcel的相应Parcelable API)被devise为高性能IPC传输。 因此,将任何Parcel数据放入持久性存储中是不恰当的:Parcel中任何数据的底层实现的更改都会导致旧数据不可读。

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

我发现Parcelable在数据Bundle中最常用于Android,但更具体地说,在发送和接收消息的Handler中。 例如,您可能需要在后台运行AsyncTaskRunnable ,但将结果数据发布到主线程或Activity

这是一个简单的例子。 如果我有一个Runnable ,如下所示:

 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()); } } } 

正如你所看到的,这个runnable在其构造函数中使用了一个Handler。 这是从这样的一些Activity调用:

 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(); } 

现在,剩下的就是这个问题的核心,你如何将一个类定义为Parcelable 。 我select了一个相当复杂的类来显示,因为有些东西你不会看到一个简单的东西。 这里是ProductInfo类,Parcel和unParcel干净地:

 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; } } 

CREATOR是至关重要的,正如所得到的构造函数采用Parcel。 我包含了更复杂的数据types,所以你可以看到Parcel和unParcel数组的Parcelable对象。 当使用Gson将JSON转换为带有子对象的对象时,这是很常见的事情。

为了更好地理解包裹概念请尝试下面的链接

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

希望这可以帮助 :)

我也有类似的问题。 只有从emmby以下片段, 这帮了我。

  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(); } 

它应该保存在实现Parcelable的每个类中