When does Android serialize objects?

1.4k Views Asked by At

I am working on an Android project, and I want to pass a custom class MainActivityModel to a Fragment, MainActivityPlaceholderFragment.

I have made MainActivityModel serializable:

public class MainActivityModel implements Serializable{

    public int current = 0;
    public int pageCount = 0;

    public boolean pristine = true;

    // Stores the fetched dataMap
    public ArrayList<HashMap<String, String>> arrayList;

    public MainActivityModel() {
        this.arrayList = new ArrayList<>();
    }

    public String getCategory() {
        return Util.categories[current];
    }

    public CharSequence getmTitle () {
        return  Util.toTitleCase(
                Util.mapCategoryPretty(Util.categories[current]));
    }
}

and I am passing it to the Fragment like this:

public static MainActivityPlaceholderFragment newInstance(MainActivityModel mainActivityModel) {
    MainActivityPlaceholderFragment fragment = new MainActivityPlaceholderFragment();
    Bundle args = new Bundle();
    args.putSerializable(ARG_DATA_MODEL, mainActivityModel);
    fragment.setArguments(args);
    Log.v(LOG_TAG, "Created: " + mainActivityModel.getmTitle());
    return fragment;
}

I access it like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mainActivityModel = (MainActivityModel) getArguments().getSerializable(ARG_DATA_MODEL);
    mMainActivityPlaceholderFragmentView = new MainActivityPlaceholderFragmentView(this, mainActivityModel);

    mCallbacks.onPlaceholderFragmentCreated(mainActivityModel.current);
}

I initially thought (after reading the answers I mention below), that serialization converts data to bytes and restores them subsequently when needed. So my object would be copied. This is ok if I only want to access the data. But I also wanted to modify the actual model (which is referenced in MainActivity) from the fragment.

To experiment, I set pristine to false in the Fragment, and logging that in MainActivity, it was indeed false.

But if serializable is pass-by-value, how is this happening?

What I've read:

  1. What is object serialization?
  2. R: Pass by reference
  3. what is Serializable in Java
  4. What is serialization in Java?
4

There are 4 best solutions below

8
On BEST ANSWER

A reference to a Serializable object is still an object reference, it's no different from passing a List object or a Foo object. The confusing part is if and where the serialization takes place.

From the documentation of android.app.Fragment.setArguments(Bundle):

The arguments supplied here will be retained across fragment destroy and creation.

There are two ways to achieve this:

  • Make Bundle only store raw bytes, and serialize/deserialize for every get/put operation.
  • Allow Bundle to hold live objects, and ask it to serialize/deserialize everything when the fragment needs to be destroyed/recreated.

Clearly, the first option is very inefficient: get/put operations are much more frequent than activity/fragment life cycle changes. Therefore, Android will only serialize/deserialize when needed on life cycle changes.

This causes the "weird" behavior in your use case. You assumed that your Serializable object is serialized immediately by Bundle, where instead the Bundle simply holds a reference to your object. Since the fragment is not destroyed between the newInstance and onCreate call, you are seeing the exact same Bundle holding the exact same references.

Of course, you should not rely on these references to stay intact. Any time your application is asked to persist its state (e.g. when going to the background, when rotating the screen, or when the system needs to free up RAM), those objects are serialized and the references are gone. The objects will be re-created from the serialized data, but they will have different references.

0
On

In Java all objects are passed by reference, only primitive types (int,float,long...) are passed by value.

I doesn't really know how Serializable works, but if its converting to byte[] that isn't a primitive type so that's why it is working.

If you write a Serializable class to a file and open it as ASCII you would see kind of a recursive toString() followed by what could probably be the data as bytes.

Hope this helps.

2
On

Serialization creates deep copies of your objects, meaning that if you serialize then deserialize an object containing other objects, you will get new independent objects (with new references), copies of everything, with absolutely no reference to the objects you still had on the heap. Thus, if you modify the objects you have just deserialized, you will only modify these objects, not any previous reference you could have on the heap.

If you want to reconciliate references you have just deserialized with your objects in memory, you have to code it.

0
On

The behavior you are describing looks a lot like Parcel active objects, not sun-java-style serialization.