In some activity, I have to save MVC model as a parcelable. The parcelable is built according to the doc, which I've read more than enough (but who knows, I obviously could have missed something). There is a leak in this activity, but I'm struggling to understand its cause. The SO question Communication objects between multiple fragments in ViewPager was interesting but my code was already following guidelines from the answer.
The activity own a viewpager with around 60 fragments inside (but up to 350). The model is passed from the activity to all fragments, and user actions in the fragments are saved into the model.
Whenever I pause my activity, onSaveInstanceState
is triggered once, and immediately after multiple triggers of my parcelable's writeToParcel
method. The number of triggers depends on the number of Fragments ever loaded in the viewpager
+ 1. So at activity startup, if I turn the emulator off and back on, writeToParcel
is called 3 times (only 1st and 2nd fragment are loaded), if I swipe once right and do it again, it is called 4 times (the 2nd is showing and the 3rd is loaded), if I setExtPosition()
on the adapter and go to 10th fragment, writeToParcel
is called 7 times (9th, 10th and 11h are loaded).
Of course if my user swipe every fragment, it will eventually get an ugly TransactionTooLargeException
, which brings me here.
Here is some code. There may be a ton of code/concept improvements here, and any tips is very welcome, but my main problem is this dirty little leak I've found.
In my activity:
@Override
public void onSaveInstanceState (Bundle outState) {
outState.putParcelable("model", myParcelable);
super.onSaveInstanceState(outState);
}
In my fragment:
public static MyFragment newInstance(Model model) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putParcelable(KEY_MODEL, model);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
mModel = args.getParcelable(KEY_MODEL);
}
In my parcelable model:
@Override
public void writeToParcel(Parcel dest, int flags) {
int startSize = dest.dataSize();
dest.writeString(foo); //one of these string is supposed to be null
dest.writeString(bar);
dest.writeString(foobar);
dest.writeByte((byte) (isMyObjectTrue ? 1 : 0));
dest.writeList(someStringList);
dest.writeList(someIntegerList);
dest.writeBundle(someBundle); //I use a Bundle to save a Map<String,String>
int endSize = dest.dataSize();
}
I ran the debugger inside the writeToParcel()
method, and I was surprised to see that startSize
is never 0. Is it normal ?
I searched throughout my code, and putParcelable()
or any writing method with parcelable in its name is only called in this activity and in the fragment newInstance()
.
How can I find the cause of this weird exponential behaviour ?
PS: of course feel free to ask for more code.
EDIT
I've implemented the solution advised by @Ben P., and the problem have improved a lot, but is not totally solved. My activity implements an interface which now has a getModel()
method called in onAttach()
, and a setUserInput(char userInput)
I use to update the model from the fragment. The fragment's newInstance()
method don't save the model anymore.
MyFragment
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
callBack = (MyInterface) context; //callBack is a class field
mModel = callBack.getModel(); //mModel too
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement MyInterface");
}
}
This turned the exponential problem into a linear problem which is obviously better but still a problem.
Now, writeToParcel()
is only called once, but the total parcel size is still growing with the number of item loaded. My model takes around 3kb inside the parcel (+/-10% depending on the number of inputs), as measured by endSize-startSize
.
How can I know where the growth comes from ?