How to use Parcel in Android?

I am trying to use Parcel to write and then read Parcelable . For some reason, when I read the object back from the file, it comes 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 deleted reading and writing files in my original example.

+50
android parcel
Oct 26 '09 at 18:53
source share
5 answers

And finally, I found the problem. There were actually two.

  • CREATOR should be publicly available, not secure. But more importantly,
  • You must call setDataPosition(0) after canceling your data search.

Here is the edited 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); } } 
+62
Nov 05 '09 at 2:58
source share

Caution! Do not use Parcel to serialize to a file

A package is not a general purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects in Parcel) is designed as a high-performance IPC transport. Thus, it is not advisable to put any Parcel data in a permanent storage: changes in the basic implementation of any of the data in the Package can make the old data unreadable.

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

+20
Jun 08 '10 at 15:50
source share

I found that Parcelable is most often used in Android in data packets, but more specifically in a handler that sends and receives messages. For example, you may have AsyncTask or Runnable , which should run in the background, but send the received data to the main thread or Activity .

Here is 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 accepts a handler in its constructor. This is called from some Activity as follows:

 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 remains is the heart of this question, how do you define a class as Parcelable . I chose a rather complicated class to show, because there are some things that you would not see with a simple one. Here is the ProductInfo class, which is clean and unParcels clean:

 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 is critical, as is the resulting constructor that takes the parcel. I have included more complex data types so you can see how Parcel and UnParcel arrays are Parcelable arrays. This is a common thing when using Gson to convert JSON to objects with children, as in this example.

+12
Nov 19 '13 at 16:58
source share

To better understand the concept of "Parcel", try the link below

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

hope this helps :)

+5
Feb 23 2018-11-23T00:
source share

I also had a similar problem. Only the following snippet from emmby also helped me.

  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 stored in every class that implements Parcelable.

+1
Dec 11 '15 at 9:46
source share



All Articles