Using the Android Parcel
2017-05-09 12:41
211 查看
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.
We need to collect that information from our data collection screen to process it. So all we do is the following:
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 the second Activity:
And this is what a Parcelable User class looks like:
What we did was:
Make our User class implement the Parcelable interface. Parcelable is not a marker interface, hence what follows:
Implement its describeContents method, which in this case does nothing.
Implement its abstract method writeToParcel, which takes the current state of the object and writes it to a Parcel
Add a static field called CREATOR to our class, which is an object implementing the Parcelable.Creator interface
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:
Isn’t using an IPC-friendly, custom object for in-process communication simply overkill?
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).
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. screen Intent in = new Intent(this, ProcessDataActivity.class); in.putExtra("userid", id); // (key,value) pairs in.putExtra("age", age); in.putExtra("phone", phone); in.putExtra("is_registered", true); // call next Activity --> next screen comes up startActivity(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 Activity Intent 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 methods User usr = new User(); usr.setId(id); // collected from user input// etc.. // pass it to another component Intent 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 Ctor public User(){} // all getters and setters go here //... /** Used to give additional hints on how to process the received parcel.*/ @Override public int describeContents() { // ignore for now return 0; } @Override public 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:
Make our User class implement the Parcelable interface. Parcelable is not a marker interface, hence what follows:
Implement its describeContents method, which in this case does nothing.
Implement its abstract method writeToParcel, which takes the current state of the object and writes it to a Parcel
Add a static field called CREATOR to our class, which is an object implementing the Parcelable.Creator interface
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:
Isn’t using an IPC-friendly, custom object for in-process communication simply overkill?
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).
相关文章推荐
- Android C native development using the NDK under Windows
- Android: How to download the latest zip Android Source Code easily and using it in Intellij
- Using the Android action bar (ActionBar) - Tutorial
- Using the LZO Parcel
- Android Material Design-Using the Material Theme(使用Material主题)-(二)
- (ZT) Using the Android NDK
- Android异常汇集----3.java.lang.NoClassDefFoundError: Class not found using the boot class
- Open the Android native Camera using OpenCV
- Android 自定义控件build时提示Custom view * is not using the 2- or 3-argument View constructors; XML attribut
- No instrumentation runner found for the launch, using android.test.
- Using the Android Application class to persist data
- Warning: No instrumentation runner found for the launch, using android.test.InstrumentationTestRunner
- Warning: No instrumentation runner found for the launch, using android.test.InstrumentationTestRunne
- Augmented Reality on Android: Using GPS and the Accelerometer
- what is the purpose of using translatable in Android strings?
- Warning: No instrumentation runner found for the launch, using android.test.InstrumentationTestRunner
- android ---Using java surface on the native side
- No instrumentation runner found for the launch, using android.test.
- Android Material Design-Using the Material Theme(使用Material主题)-(二)
- when not using the appcompat library, you should be using the android:showAsAction attribute.