4

I have a class that implements Parcelable.

I safe this object to the bundle in an activites onSaveInstanceState and I try to recreate the object in onCreate. This looks like following:

@Override
protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null)
    {
        // this line does NOT call my constructor, why not?
        // AND I don't get the same object as before => I get an uninitialised object
        mStackManager = savedInstanceState.getParcelable(BackstackManager.TAG);
        // the list and map in the object are NULL after this...
    }
    else
    {
        mStackManager = new BackstackManager();
        ...
    }
} 

@Override
public void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    outState.putParcelable(BackstackManager.TAG, mStackManager);
}

When the Parcelable class is created, it should call the cosntructor of the parcelable class, shouldn't it? My parcelable class is retrieved from the bundle, but it does not look like the object that was saved, it even is not initialised...

My simplified parcelable class looks like following:

public class BackstackManager implements Parcelable
{
public static final String TAG = BackstackManager.class.getName();

private List<List<Pair<Class<?>, Pair<Integer, String>>>> mBackstack;
private Map<String, Fragment.SavedState> mSavedStateMap;

public BackstackManager()
{
    init();
}

private void init()
{
    mBackstack = new ArrayList<List<Pair<Class<?>, Pair<Integer, String>>>>();
    mSavedStateMap = new HashMap<String, Fragment.SavedState>();
}

// ----------------------
// Parcelable
// ----------------------

public static final Parcelable.Creator<BackstackManager> CREATOR =
        new Parcelable.Creator<BackstackManager>()
        {
            @Override
            public BackstackManager createFromParcel(Parcel source)
            {
                return new BackstackManager(source);
            }

            @Override
            public BackstackManager[] newArray(int size)
            {
                return new BackstackManager[size];
            }
        };

public BackstackManager(Parcel source)
{
    init();
    readFromParcel(source);
}

@Override
public int describeContents()
{
    return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags)
{
    dest.writeInt(mBackstack.size());
    for (int i = 0; i < mBackstack.size(); i++)
    {
        List<Pair<Class<?>, Pair<Integer, String>>> data = mBackstack.get(i);

        dest.writeInt(data.size());
        for (int j = 0; j < data.size(); j++)
        {
            Class<?> clazz = data.get(j).first;
            Integer id = data.get(j).second.first;
            String tag = data.get(j).second.second;

            Fragment f = mFragmentManager.findFragmentByTag(tag);

            // 1) save backstack data
            ParcelBundleUtils.writeStringNullSafe(dest, clazz != null ? clazz.getName() : null);
            dest.writeInt(id);
            dest.writeString(tag);

            // 2) save fragment state, if possible
            ParcelBundleUtils.writeBoolean(dest, f != null);
            if (f != null)
                ParcelBundleUtils.writeParcelableNullSafe(dest, mFragmentManager.saveFragmentInstanceState(f), 0);
        }
    }
}

public void readFromParcel(Parcel source)
{
    int backstackSize = source.readInt();
    for (int i = 0; i < backstackSize; i++)
    {
        ArrayList<Pair<Class<?>, Pair<Integer, String>>> data = new ArrayList<Pair<Class<?>, Pair<Integer, String>>>();
        int dataSize = source.readInt();
        for (int j = 0; j < dataSize; j++)
        {
            // 1) read backstack data
            String clazzName = ParcelBundleUtils.readStringNullSafe(source);
            Class<?> clazz = null;
            if (clazzName != null)
            {
                try
                {

                    clazz = Class.forName(clazzName);
                }
                catch (ClassNotFoundException e)
                {
                    L.e(this, e);
                }
            }
            Integer id = source.readInt();
            String tag = source.readString();

            // 2) read fragment state if possible
            boolean savedStateExists = ParcelBundleUtils.readBoolean(source);
            if (savedStateExists)
            {
                SavedState state = ParcelBundleUtils.readParcelableNullSafe(source, SavedState.class.getClassLoader());
                if (state != null)
                    mSavedStateMap.put(tag, state);
            }

            data.add(new Pair<Class<?>, Pair<Integer, String>>(clazz, new Pair<Integer, String>(id, tag)));
        }
        mBackstack.add(data);
    }
}
}

My ParcelBundleUtils just write a bit if an object is null or not and dependent on that read/write the actual object...

EDIT

my current workround is to use a read/write function for a bundle that does EXACTLY (I copy and pasted my code and just replaced the read/write functions) the same as the parcel read/write function, but writes the data directly to a bundle... But I really would like to know, why the above is not working as a parcel

EDIT 2

I found following: Android: Parcelable.writeToParcel and Parcelable.Creator.createFromParcel are never called

But if that's the problem, I would not be the only one facing this problem...

Community
  • 1
  • 1
prom85
  • 16,896
  • 17
  • 122
  • 242
  • Your `init()` method should be called. Please post the code in `readFromParcel()` and `writeToParcel()` – David Wasser Nov 04 '14 at 14:10
  • debugging shows, that it is not called... neither of the constructors... and I added the read and write functionality code – prom85 Nov 04 '14 at 14:21
  • You're saying that `mStackManager` is in fact not `null` after you call `getParcelable`? – Jeffrey Mixon Nov 04 '14 at 16:44
  • Sounds very strange. I've looked at this question and your code a dozen times now and can't see anything wrong with it. Your explanation makes no logic sense. – David Wasser Nov 04 '14 at 16:46
  • Exactly... `mStackManager` is not null... but the internal list and map are null... Some other strange detail: I have a listener which of course is not saved to the parcel... this is not null after calling `getParcelable`... Although it should of course... – prom85 Nov 04 '14 at 16:58
  • what I found is following: http://stackoverflow.com/questions/4853952/android-parcelable-writetoparcel-and-parcelable-creator-createfromparcel-are-ne. It may have to do with that... – prom85 Nov 04 '14 at 17:06
  • Oh, that is nasty :-( – David Wasser Nov 05 '14 at 10:17
  • 1
    I found the problem... I my activities `onDestroy` I cleaned my object (I thought, the object is saved to the bundle anyway and will be recreated afterwards)... That was wrong, I just should give up my references, nothing more. Because the bundle will only parcel my object, if necessary, NOT ALWAYS... It does not parcel it on screen rotation for example... – prom85 Nov 06 '14 at 15:34

1 Answers1

0

You must have the following line as the first line in your onCreate(Bundle) override:

 super.onCreate(savedInstanceState);

This is why your state is not working correctly.

Jeffrey Mixon
  • 12,846
  • 4
  • 32
  • 55
  • Actually, I use an activity... You are right, that would be a problem, but that would make my workaround fail as well... I forgot to add the `super.onCreate(savedInstanceState)` call, because I'm using some `BaseActivity` which splits the `onCreate` in `onBeforeCreate` and `onAfterCreate`, therefore my base class does always call the `super.onCreate(savedInstanceState)`... – prom85 Nov 04 '14 at 16:20