如何在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了问题。 事实上有两个。
- 创作者必须是公开的,不受保护。 但更重要的是,
- 您必须在解组数据后调用
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中。 例如,您可能需要在后台运行AsyncTask
或Runnable
,但将结果数据发布到主线程或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的每个类中