Using the Android Parcel

来源:互联网 发布:金万维免费域名 编辑:程序博客网 时间:2024/06/01 08:53

A short definition of an Android Parcel would be that of a message container for lightweight, high-performance Inter-process communication (IPC). On Android, a “process” is a standard Linux one, and one process cannot normally access the memory of another process, so with Parcels, the Android system decomposes objects into primitives that can be marshaled/unmarshaled across process boundaries.

But Parcels can also be used within the same process, to pass data across different components of a same application. As an example, a typical Android application has several screens, called “Activities” , and needs to communicate data or action from one Activity to the next. To write an object than can be passed through, we can implement the Parcelable interface. Android itself provides a built-in Parcelable object called an Intent which is used to pass information from one component to another.

Using an Intent is pretty straightforward. Let’s say we’re collecting user data from our initial screen called CollectDataActivity.

// inside CollectDataActivity, construct intent to pass along the next Activity, i.e. screenIntent in = new Intent(this, ProcessDataActivity.class);in.putExtra("userid", id); // (key,value) pairsin.putExtra("age", age);in.putExtra("phone", phone);in.putExtra("is_registered", true);// call next Activity --> next screen comes upstartActivity(in);

We need to collect that information from our data collection screen to process it. So all we do is the following:

// inside ProcessDataActivity, get the info needed from previous ActivityIntent in = this.getIntent();in.getLongExtra("userid", 0L);in.getIntExtra("age", 0);in.getStringExtra("phone");in.getBooleanExtra("is_registered", false); // false = default value overridden by user input

Again, pretty straightforward. We retrieve the data using the same keys used to send it, and using our Intent’s corresponding methods for each data type. But even when communicating with Intents, we can still use Parcels to pass data within the intent. For instance, we can do the above in a more elegant way using a custom, Parcelable User class:

In the first Activity:

// in CollectDataActivity, populate the Parcelable User object using its setter methodsUser usr = new User();usr.setId(id); // collected from user input// etc..// pass it to another componentIntent in = new Intent(this, ProcessDataActivity.class);in.putExtra("user", usr);startActivity(in);

In the second Activity:

// in ProcessDataActivity retrieve User Intent intent = getIntent();User usr = (User) intent.getParcelableExtra("user");

And this is what a Parcelable User class looks like:

import android.os.Parcel;import android.os.Parcelable;public class User implements Parcelable {private long id;private int age;private String phone;private boolean registered;// No-arg Ctorpublic User(){}// all getters and setters go here //.../** Used to give additional hints on how to process the received parcel.*/@Overridepublic int describeContents() {// ignore for nowreturn 0;}@Overridepublic void writeToParcel(Parcel pc, int flags) {pc.writeLong(id);pc.writeInt(age);pc.writeString(phone);pc.writeInt( registered ? 1 :0 );   }   /** Static field used to regenerate object, individually or as arrays */  public static final Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>() { public User createFromParcel(Parcel pc) { return new User(pc); } public User[] newArray(int size) { return new User[size]; }   };   /**Ctor from Parcel, reads back fields IN THE ORDER they were written */   public User(Parcel pc){   id = pc.readLong();   age=  pc.readInt();   phone  = pc.readString();   registered = ( pc.readInt() == 1 );  }}

What we did was:

  1. Make our User class implement the Parcelable interface. Parcelable is not a marker interface, hence what follows:

    1. Implement its describeContents method, which in this case does nothing.
    2. Implement its abstract method writeToParcel, which takes the current state of the object and writes it to a Parcel
    3. Add a static field called CREATOR to our class, which is an object implementing the Parcelable.Creator interface
    4. Add a Constructor that takes a Parcel as parameter. The CREATOR calls that constructor to rebuild our object.

This looks like a lot of extra code at first, but bear in mind that, as in most cases, our application might evolve into incorporating more data from the user… Sometimes we need to pass complex objects from one component to another, and passing an object yields a cleaner design.

The same logic applies for communicating between an Activity (foreground UI) and a background Service. We would just call the startService method instead of startActivity and pass it our Parcelable User object. Note that a Service is not running in a separate process by default.

At this point, there are a couple of questions that may be raised:

  1. Isn’t using an IPC-friendly, custom object for in-process communication simply overkill?

    1. Why would we want to use Parcelable, when we already have built-in Java serialization?

The answer to the first concern is…maybe. But communicating through a custom object than through a list of key-value pairs is more OO, and it has no noticeable negative performance impact.

As for the second question, why not simply have User implement Serializable, a theoretically simpler, marker interface? In one word, performance. Using Parcels is more efficient than serializing, at the price of some added complexity.

That extra efficiency has in turn its limits: passing an image ( Bitmap) using Parcelable is generally not a good idea (although Bitmap does in fact implement Parcelable). A much more memory-efficient way would be to pass only its URI or Resource ID, so that other Android components in your application can have access to it.

Another limitation of Parcelable is that it must not be used for general-purpose serialization to storage, since the underlying implementation may vary with different versions of the Android OS. So yes, Parcels are faster by design, but as high-performance transport, not as a replacement for general-purpose serialization mechanism.

Having said all that, since our User object is Parcelable, it can now be sent from this application to another one running in another process, in particular through an interface implementing a remote service. In an upcoming post, we’ll look at IPC and Android’s Interface Definition Language (AIDL).

0 0