Post navigation

Fragment view state retention: A dirty solution

This is the last part of this 6 part series about Fragment Oriented Architecture in Android applications. In the previous post I talked about managing sessions in fragment oriented application. In this post I am going to talk about retaining view hierarchy of a Fragment after removing it from container and then coming back to it by popping the backstack.

When a fragment gets replaced by another fragment and the transaction is added to back stack, the expectation after a popBackStack() is to return to the previous fragment with its UI state intact.Â Activity backstack takes care of this expectation quite cleanly until a low-memory situation occurs. But in case of fragments, thisÂ isn’t the default behaviour. In a vanilla implementation,Â the replaced fragment’s view-hierarchy would get recreated upon returning back to it. Reason is that during a replace operation, all the destructive life-cycle methods get calledÂ till onDestroyView(), which wipes out the view-hierarcy. Upon returning back, all the constructive lifecycle methods rightÂ from onCreateView() get called, thus, recreating the view-hierarchy totally afresh.Â Reason for this flow is to keep ‘Fragments’ memory friendly. Without the view-hierarchy, a fragment is just a java object with a bunch of instance variables.

So, the good news is we still have the instance variables intact. For instance, if user updates text in an EditText to “Some text”, we can keep this value in an instance variable before a replace operation and upon returning back, this value can be set as the text of EditText in new view hierarchy. Same can be done with scroll-state, switch state, etc. This is the recommended way and pretty clean. But this solution can get exponentially complicatedÂ with increase in complexity of fragment’s view-hierarchy.

Dirty solution is to retain the view-hierarchy upon a replace operation. This of course dumpsÂ the ‘memory-friendly’ tag but helps a great deal whileÂ implementingÂ complicated views. This is done by keepingÂ a reference to the root-view of fragment before replace operation and returnÂ it fromÂ onCreateView() instead of inflating a fresh view-hierarchy.Â This can be done by the following additional code in BaseFragment.

// PERSISTENT ROOT VIEW
public abstract class BaseFragment extends Fragment {
public boolean hasInitializedRootView = false;
private View rootView;
public View getPersistentView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState, int layout) {
if (rootView == null) {
// Inflate the layout for this fragment
rootView = inflater.inflate(layout, null);
} else {
// Do not inflate the layout again.
// The returned View of onCreateView will be added into the fragment.
// However it is not allowed to be added twice even if the parent is same.
// So we must remove rootView from the existing parent view group
// (it will be added back).
((ViewGroup)rootView.getParent()).removeView(rootView);
}
return rootView;
}

getPersistentView() method in above code keeps a reference to the view-hierarchy and never inflates it again. This method can be called from onCreateView() callback methods of inheriting fragments that have a complex UI.

In sample application, last two sectionsÂ are using this technique to retain scroll position of list. NestedListFragment is an even more complex situation where the list is a nested child fragment. Handling this situation in recommended ways is quite a pain.

This, of course, is not an ideal solution. I’m still looking for a better one. Please direct me if you hit upon a better solution.

6 thoughts on “Fragment view state retention: A dirty solution”

I’m puzzled about how view state is handled by Android. I thought view state would be lost along with onDestroyView() but I changed your sample app to not use getPersistentView() and the list view scroll position was persisted anyway. Also the Inter Fragment Communication (Greetings) feature of the sample app persists the contents of the EditText across backstack transactions (even if I don’t keep an EditText instance attribute in the fragment); shouldn’t it be empty when the backstack is popped back? I wrote a test app which provides the same feature and its behavior is the same. I submitted a question on StackOverflow (http://stackoverflow.com/questions/28344678/fragments-ondestroyview-called-but-view-is-not-being-destroyed) but I also wonder what you think about this situation.

I’ve stumbled upon this approach to retain the fragment view start and was quite fascinated at the aspect of retaining the view of the fragment without much hassle until i faced weird behaviors on the UI elements after returning back to the fragment from the backstack. I happen to use view Injectios (robojuice or butterknife) the most common problem is some views are not being updated after view is retained, can this be perhaps of reinitializations by the injection libraries ? any idea ?