Photo courtesy: Google

The Activity/Fragment monolith is bad in itself but it makes it worse when there is all this data you fetch and then the screen rotates! If you store the data in an Activity class, it’ll be gone! This is what happened to one of the projects I worked on. The app was written with its orientation being locked in portrait. So the Activity class did all the work of fetching the data, applying some business logic on it and then even displaying it and it worked fine. But then came the dreaded product / design requirement; to support rotation. The product wanted something quick and they thought it’s as easy as just turning off the rotation lock! As Android devs, we knew it was not something as easy as that.

Options

When deciding on options, the obvious and cleaner solution was to adopt a pattern like MVP or MVVM and breaking the monolith. One good thing about the app was that it was well organized. The data access objects (DAO) and the data classes (DTO) were very well defined. So, it sure made our lives a bit easy but to adopt a pattern in the given time and resource constraints didn’t seem like the best option. One quick and dirty option was to selectively store the data we want to retain in the Application class since it’s alive as long as the app lives. But that wasn’t enough because we’d also have to clear the data when we no longer need it and determining that would be an added task for all the current and future Activities. So this option was also discarded.

Headless Fragments to rescue

I read Alex Lockwood’s article on using such Headless Fragments (the reason for the name is pretty straightforward. They are fragments without any UI) for executing long running tasks and then passing the data back to Activity using WeakReferences . I also came across this article that uses the Headless Fragments for permissions in Android M.

Headless Fragments are basically Fragments with setRetainInstance() set to true *. So when the Fragment is created, we tell Android to retain it’s instance and it retains even on rotation!

For problem at hand, we decided to use this approach to store the data that we fetch for the Activity / Fragment in these Headless Fragments so that it survives the rotation. Let’s look at an example below. Let’s assume that we fetch a List<String> which we want to retain across rotation.

The first step is to attach this Headless Fragment to the Activity . Here is how:

public class ExampleActivity extends AppCompatActivity { private ExampleActivityStateFragment mStateFragment; @Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



FragmentManager fm = getSupportFragmentManager();

mStateFragment = (ExampleActivityStateFragment) fm.findFragmentByTag(KEY_STATE_FRAGMENT);

if (mStateFragment == null) {

mStateFragment = new ExampleActivityStateFragment();

fm.beginTransaction().add(mStateFragment, KEY_STATE_FRAGMENT).commit();

}

}

}

The above block checks to see if we have already created a StateFragment (we called it StateFragment in code) and if so, we can just find that Fragment by the unique TAG for each Fragment . If not, we create one and attach it to the Activity .

Here is what the StateFragment looks like:

public static class ExampleActivityStateFragment extends Fragment { private static final String TAG = "custom_activity_state"; // This is where we store the data we need to be retained

private List<String> mList; @Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setRetainInstance(true);

}



}

Now, with this setup, here is how we save the data with the StateFragment .

public class ExampleActivity extends AppCompatActivity { private ExampleActivityStateFragment mStateFragment; @Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);



FragmentManager fm = getSupportFragmentManager();

mStateFragment = (ExampleActivityStateFragment) fm.findFragmentByTag(KEY_STATE_FRAGMENT);

if (mStateFragment == null) {

mStateFragment = new ExampleActivityStateFragment();

fm.beginTransaction().add(mStateFragment, KEY_STATE_FRAGMENT).commit();



}

} protected void getData() {

if (mStateFragment.mList == null) {

mStateFragment.mList = getListOfStrings();

}

} protected void drawUI() { // do something with the list

mRecyclerView.setAdapter(mStateFragment.mList)

} }

In the above code, the method getData() fetches the data (either based on user action or onCreate() ) and writes it to the StateFragment’s mList variable the first time it runs. When the Activity is rotated and getData() is called again, the StateFragment’s mList won’t be null , because the StateFragment retained its instance and is not killed like its parent Activity . And bingo! we save the data across rotation. There is a 1:1 relationship between the Activity and its StateFragment .

You can also have the StateFragment as an inner class, but then it becomes very easy to reference the Activity context from the StateFragment and thus leading to memory leaks if you’re not careful.

Notes

* One important thing to note about this is that it’s very easy to go on the slippery slope of saving Activity reference or reference to Views in the StateFragment . But it’s highly discouraged to do so. There are ways to avoid memory leaks by setting the views to null in onDestroy() in a pure technical sense, but it’s not a good practice overall. So please be aware of that! In our case, because we don’t store any reference to the caller Activity / Fragment or any Views, the StateFragment can be safely garbage collected when the Activity is destroyed.

It is definitely better to follow an architecture pattern for cleaner implementation and handling lifecycle events. With Android’s new Architecture Components, adapting a pattern becomes a bit easy. In fact if you observe the code in the android.arch.lifecyle package, you’ll notice that the basis of ViewModelProviders that provides the ViewModel to store data across configuration changes is, none other than, setRetainInstance(true) ! :)

Conclusion