Gil's profile at Stack Overflow Coding challenges at Project Euler



The retained fragment trick

The first tough problem a new Android developer faces is how to manage work in the background. Spawning a new Thread is easy, but once UI objects need to be notified with the result, the situation becomes tricky: the UI objects might have been replaced by the system because of a configuration change. In this post we explore how to bind and rebind to the new UI objects with a retained Fragment.

The common non-solution to this problem is AsyncTask. Since we cannot perform long tasks on the UI thread, some Android framework engineer, namely Romain Guy, came up with the idea to encapsulate a background thread and a callback on the UI thread into a class. So we don't need anymore to move the data between threads ourselves, with all thread safety implications of the case, but we can just rely on AsyncTask. The problem with it is that AsyncTask is just that, a nice wrapper over some threading logic. In particular it does not interact with Activities or Fragments whatsoever.

So what to do? The legacy solution was to use the callback onRetainNonConfigurationInstance(). This callback is called between onStop() and onDestroy() and, whatever object you return, it will be retrievable in the next Activity instance by the method getLastNonConfigurationInstance(). In particular you can return whole Threads or AsyncTasks. I suggest that you read carefully the documentation of those methods as they provide insight how messages on the UI thread are processed.

The modern solution is to use Fragments. Actually the callbacks were doing just fine, but their deprecations suggest that Google want to push Fragments to modularize apps and to promote reuse. So the new solution to our problem is to put the object that we had returned from onRetainNonConfigurationInstance() into a retained Fragment. The Fragment then can be found by its enclosing Activity going through the FragmentManager. Alternatively, a Fragment manages a reference to its enclosing activity through the method getActivity().

This is basically the solution, retained Fragment plus getActivity(). To make a Fragment 'retained' just call setRetainInstance(true) like this:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true); 
}

To illustrate the approach better I created a more complex example, where I reduce the boilerplate leveraging Dagger 2, a dependency injection framework by Google. The examples displays a Button and a TextView with a number, both living in a UI Fragment. When the user presses the button, an AsyncTask is created and executed. The AsyncTask sleeps for some second, then it comes back on the UI and it adds one to the number in the TextView. The binding and rebind of the UI Fragment to the AsyncTask is done by the retained Fragment. To test this, just try tilting the device during the background sleep: after the configuration change, the number will be still updated. The sample can be found here.