Where Did My Static Data Go?

Are you storing stuff in singletons? Are you storing things in your applications class (e.g. dagger ObjectGraphs/Components or Guice Injectors)? Do you cache data in your http client?

How does your application handle not having this data part way through your UI flow?

Did you know that Android will destroy your process under low memory conditions, but when you return to the application it will restore your instance state in an entirely new process? This gives you new static variable instances. It means you have lost any dagger ObjectGraphs/Components or Guice Injectors that were not re-created in Application#onCreate() in your new Application instance (e.g. a session graph that was created when you logged in). This means that all your non-disk cached data is gone!

Here's the quote from the Process Lifecycle section in the Activity documentation:

Process Lifecycle

Process Lifecycle 3. A background activity (an activity that is not visible to the user and has been paused) is no longer critical, so the system may safely kill its process to reclaim memory for other foreground or visible processes. If its process needs to be killed, when the user navigates back to the activity (making it visible on the screen again), its onCreate(Bundle) method will be called with the savedInstanceState it had previously supplied in onSaveInstanceState(Bundle) so that it can restart itself in the same state as the user last left it.

How does your application respond to this situation?

This is perhaps something that a lot of people don't consider or handle because it's hard to test, but if you are writing any sort of high quality Android application, this is something you need to consider. Fortunately, a developer called Oisin O'Neill recently posted a great tool for helping to test this situation Android Developer Toolbelt.

Having this tool is just the first step though - the hard part is fixing your problems if you have any. For example, if you rely on data that you acquired through a network call, what do you do when your app is restarted and that data no longer exists? Does your app correctly show progress loading and retrieve that data from the network again mid flow? Or does it handle it by logging the user out and restarting the app completely? The latter might be easier in some cases, but obviously more annoying for users who use low memory devices on a day-to-day basis.

All that said - I have had to revisit my code in my hello-mvp app, which relied on a singleton cache of presenters. Let's discuss the changes:

It's apparent now that any piece of presentation code that can hold state needs a way to save/restore that state across processes. We can easily save the state of our presenters alongside our fragments. To do this, I've added 2 new methods to the Presenter interface:

public interface Presenter<T> {
    void onCreate(@Nullable PresenterBundle bundle);
    void onSaveInstanceState(@NonNull PresenterBundle bundle);

onCreate gets called when a new Presenter instance is created, onSaveInstanceState gets called every time the view state is saved by Android. onCreate gets passed a PresenterBundle, which is just a non-Android copy of a Bundle and can use it to restore any state that was saved. Note that non-serializable objects, e.g. Rx Observables, will not be able to be persisted. Therefore, in onCreate, re-creating non-serializable state is on you.

The next thing I did was I removed the static cache altogether. The Activity's non-configuration instance is now controlling this. Note that this is the same mechanism that Retain Fragments use internally. You could just use Retain Fragments themselves, but this has the distinct advantage of being synchronous and more predictable. It also has the Jake Wharton seal of approval.

The last thing I did was I used composition over inheritance for all of the logic that controls the lifecycle of the presenters. This is just better programming practice. The idea was taken from the new version of AppCompat and also a similar project by Daniel Novak called Android View Model. This means that, although there is a base class available for use with Fragments from the support library, you could also roll your own using a PreferenceFragment, or a ListFragment, or an Activity, or a View, and so on.

Check out all of the code here! For anyone waiting for Part 2 of my MVP - Presenters That Survive Configuration Changes, don't worry, it is coming soon :)

Brad Campbell

Android application developer, currently working as the lead on the ANZ goMoney NZ applications. All of the opinions and code on this blog are my own, and not that of ANZ.