How To Passing Data As Object To Fragment

 

1. Intro

if you have written application in Android, you know Intent and Bundlewhich we use to send some extra data to Android application components(Activities, Services, …). Intent use Bundle internally to hold the extras. so Bundle is the key class for holding the extras.

You can put primitive types, and objects into Bundle. But for putting objects, your class has to implement either Serializable or Parcelable interfaces, and as you know, good developers prefer Parcelable because of performance.

We’re not going to go into the details of how you should implement these interfaces as there’re lots of articles out there which can explain it perfectly. In addition there’s a good plugin for Android Studio which can ease the pain of implementing Parcelable interface.

2. How does Bundle work?

Bundle uses a Map<String, Object> for holding the extras you’re putting into. so in this way there’s no difference between a Map and Bundle. but when you’re going to send data to another Activity(or any other IPC components such as Service, BroadcastReceiver,…) the Map will be marshalled into byte[] via a Parcel, and will send to destination. in destination the data will go into the same flow in reverse and the byte[] will be unmarshalled into Map<String,Object> and you can have extras by providing the same keys.

So Bundle is like a Map which can also be marshalled/unmarshalled to/from byte[]. one important note is that you cannot use this byte[] to persist your bundle, as it’s designed ONLY to transfer to components, and will not work if underlying data structure changes.

3. Will objects be sent to Fragments by value?

Product myproduct = ...
Intent intent = new Intent(context, ProductDetails.class);
intent.putExtra("product", myproduct);
startActivity(intent);

 

When you’re sending objects to another Activity(via Intent), as the communication with Activities(and also BroadcastReceivers, Services, …) are IPC, so Bundle(which is inside the Intent) will be marshalled into byte[]. so in the destination you will have NEW objects with the same content everytime.

For writing to and reading from Parcel, Parcelable.write and Parcelable.CREATOR.ceateFromParcel are called in this situation.

 

Sending object to Fragment:

Product myproduct = ...
Bundle args = new Bundle();
args.putParcelable("product", myproduct);
Fragment productDetailsFragment = new ProductDetailsFragment();
productDetailsFragment.setArguments(args);
...

 

 

When you’re putting objects into Bundle to send to Fragment(as an argument), the objects are held in the internal map of Bundle. but because sending arguments to Fragments is not an IPC process, so it doesn’t need to be marshalled to byte[], and Fragment will get the SAME object you’re sending from Activity from the internal map!!! so if you change the object, you will get the changes also in your Activity because they’re actually one object.

But what if the fragment is going to be destroyed and created again due to memory lackage by Android? in this case the Bundle will be marshalled and its byte[] will be persist until it is created again, and then the Bundle will be created by unmarshalling the saved byte[]. so a NEW object will be created for Fragment, and it’s not the same object from Activity.

Sum up: As you can see, sending objects to an Activity are by value. but for Fragments they’re passed sometimes by reference and sometimes by value. so you should always consider this and never rely on the references.

One workaround for this problem is making it identical in all situations. for this you can send a copy of your objects to Fragment and not the objects themselves. there’re some ways to do that, but one of the easiest(&fast) ways is implementing Clonable interface and using Parcel to clone your object:

 

public class Product implements Parcelable, Cloneable {
    @Override
    public Object clone() {
        Parcel parcel = Parcel.obtain();
        this.writeToParcel(parcel, 0);
        byte[] bytes = parcel.marshall();

        Parcel parcel2 = Parcel.obtain();
        parcel2.unmarshall(bytes, 0, bytes.length);
        parcel2.setDataPosition(0);
        return Product.CREATOR.createFromParcel(parcel2);
    }
    //Parcelable implementation code here ...
}

Product myproduct = ...
Bundle args = new Bundle();
args.putParcelable("product", (Product)myproduct.clone());
Fragment productDetailsFragment = new ProductDetailsFragment();
productDetailsFragment.setArguments(args);

					
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s