Does retrieving a parcelable object through bundle

2020-05-26 17:09发布

问题:

I'm passing a parcelable object to a fragment by adding into a bundle while creating the fragment. In onc instance modification to this parcelled object reflects modification in original object and in another case it is not. I'm a little baffled by this behaviour. Till now I have assumed retrieving a parcelled objects through a bundle always create new object[no sure whether it's shallow copy or deep copy].

Someone please clarify parcelable behaviour.

回答1:

I was struggling with a similar issue. At the first glance it seems that we always obtain a new deep copy from the parcelled objects. Moreover, there are even some StackOverflow answers which suggest to use Parcelable interface to clone objects. All this just increases confusion regarding the subject.

Here is what I've found after a lot of searching and googling:

  • Take a closer look at the official Parcel documentation. Here is the important quote:

An unusual feature of Parcel is the ability to read and write active objects. For these objects the actual contents of the object is not written, rather a special token referencing the object is written. When reading the object back from the Parcel, you do not get a new instance of the object, but rather a handle that operates on the exact same object that was originally written.

Ok, as you can see, there are some special objects that are not being copyed during unparceling. But this is still a bit confusing. Does it mean we have another strong reference to the original object which prevents its garbage collection? And what are the use-cases for such objects?

  • To answer the aforementioned questions I decided to look through the Android source code. The methods I was looking for are readStrongBinder and writeStrongBinder which according to the docs do not cause a new object creation when the parcels are sent/received. And I think I found the desired answer in the ResultReceiver.java class. Here is the interesting line:

    mReceiver = IResultReceiver.Stub.asInterface(in.readStrongBinder());
    

    To understand what is this line actually doing we should go to the official AIDL documentation. Here are the most important parts:

The steps a calling class must take to call a remote interface defined with AIDL:
...
5. In your implementation of onServiceConnected(), you will receive an IBinder instance (called service). Call YourInterfaceName.Stub.asInterface((IBinder)service) to cast the returned parameter to YourInterface type.

A few comments on calling an IPC service:

Objects are reference counted across processes.

So let's put all things together:

  1. The parcelled objects can be extracted without involving deep copy process.
  2. If the parcelled objects are read using readStrongBinder method no new instances are being created. We just objtain a new reference to the original object and this reference can prevent its dealllocation.
  3. To know whether our object will be deep copyed after the parcel has been received we should take a closer look at the concrete Parcelable interface implementation.
  4. Android documentation can be really confusing and it may take a lot of time to understand it correctly.

Hope this info will help you.

If you want to read about a real-world example when the confusion regarding Parcelable objects can cause serious problems check out my blog post.