Presenter surviving orientation changes with Loaders

MVP (Model View Presenter) is a trending topic in the Android community nowadays. This pattern allows you to avoid the God Activity/Fragment syndrome, better decoupling and improve testability in your applications.

You can find many posts with really good explanations about how to apply such pattern in Android applications: post1, post2 and post3.

MVP diagram

The basic idea behind the pattern is to make your Activity/Fragment become a Passive View that display given data and delegates all events to a middle man, the Presenter object. The Presenter will decide what to do for every incoming event, retrieving or updating data from the Model and preparing the data so the View can display it. The interaction with the View, in our case the Activity/Fragment, will be done indirectly via an interface that the Activity/Fragment will implement.

This improve the separation of concerns in presentation logic and facilitate automated testing since this Presenter will not have any Android code reference.

The issue with Presenters and configuration changes

Most of the developers agree on the core concepts and how to apply them on Android, but there are still some open questions. The one that we are going to talk today is what to do with the Presenter object when a configuration changes occurs. There are a few approaches:

Let the Presenter be destroyed.

In this approach the Presenter will die when the Activity/Fragment dies. If we wanna restore some state we would need to make use of onSaveInstanceState, via being the View the one that saves this data and then provide it to the Presenter upon creation, or giving the Presenter a chance to store some data by himself passing the save instance Bundle. While this approach can work with some simple data, it will get more complicated if what we want to keep is the reference to some ongoing background operation.

Keep the Presenter somewhere and restore once the component is recreated.

This idea is the one that I have seen the most. However, implementations can differ quite a bit between each other.

The easiest one, and a bit naive, would be to store the Presenter in a static reference within the Activity/Fragment. This approach will allow us to keep the Presenter instance across orientation changes but will limit the amount of living instances of the combination “Presenter — Activity/Fragment” to only one in the entire application. Imaging what would happen in a situation when we have multiple instances of the same Fragment in a ViewPager… ups!

Another solution could be to use Fragment setRetainInstance(true). Setting this property in a Fragment will certainly make that Fragment not be destroyed, giving us the change to survive the Presenter altogether. However this is not a complete solution since we cannot use it with child fragments and it is not available for Activities.

The final alternative that I have seen would be to use a singleton cache with some kind of identifiers to store different instances of the same Presenter. In order to preserve this identifier on configuration change the Activity/Fragment should make use of onSaveInstanceState. The issue here is when to remove the Presenter from the singleton cache plus all the additional logic that needs to be handled.

A new approach

As we can see, all the approaches have some caveats or corner cases that make them tricky to get right. After give some more thought and being inspired by this nice blog post about Loaders: Making loading data lifecycle aware, I would like to introduce a new approach: use Loaders to preserve Presenters during rotation.

But first… what are Loaders and what they give to us?

For an in deep explanation about how Loaders work please refer to official documentation or this nice series of blog posts by Alex Lockwood.

As we all know, by default, device configuration changes such as rotating your screen involve restarting your whole Activity. Loaders are a tool provided by the Android framework that survive configuration changes. Their lifecycle will be managed by the system and they will be automatically cleaned up when the requesting Activity or Fragment is permanently destroyed. That means no additional logic on the application side to decide when to delete them.

Originally Loaders main use case is to load some data in background, via extending AsyncTaskLoader and CursorLoader, specially shining with this one, a content provider and a SQLite database. But as the previous mentioned post stands, they could also be used without a background thread via extending the base Loader directly.

Thats great, but… what does it have to do with Presenters?

Well, as we already mention, the problem with Presenters is where to store them and when to decide that their time is over. On the other hand, we just saw a few capabilities about Loaders: they are a tool provided by the framework that is lifecycle aware, the system will take care of cleaning them up when their time has come to an end and last but not least, they don’t necessary need to run in background.

So considering our needs and the Loader capabilities, we can use Loaders as a provider for our Presenter instances without worrying about configuration changes.

use a synchronous Loader as a cache to store our Presenter”

The catch here is that by using the Loader synchronously we can be sure at which moment during the lifecycle* the Presenter will be initialized and ready to interact. All before the Activity/Fragment is visible to the user.

*Note: There is a important difference in regard to when Presenter will be delivered to Activities or Fragments. In Activities, after calling super.onStart the Presenter will be ready to use in every circumstance. However, in Fragments when first created, the Presenter will be deliver after super.onStart, but on recreation it will be delivered after super.onResume. So, on Fragments we can just rely that our Presenter will be there after super.onResume.

Other key Loader feature that make this whole idea work is how are they managed by the system. Each instance of Fragment/Activity will have their own LoaderManager, which will control the Loaders associated with this and only this specific Android component, making possible to have two instances of the same Android component with their corresponding Loader alive at the same time.

Ok ok, that sounds nice, so how we translate that to code?

First we should create our Loader implementation extending directly the Loader class. Take into account that we wont subclass CursorLoader or AsyncTaskLoader since we do not want the background capabilities that they provide. The implementation could looks as follows:

public class PresenterLoader<T extends Presenter> extends Loader<T>{

private final PresenterFactory<T> factory;
private T presenter;

// Constructor...

@Override
protected void onStartLoading() {

// If we already own an instance, simply deliver it.
if (presenter != null) {
deliverResult(presenter);
return;
}

// Otherwise, force a load
forceLoad();
}

@Override
protected void onForceLoad() {
// Create the Presenter using the Factory
presenter = factory.create();

// Deliver the result
deliverResult(presenter);
}

@Override
protected void onReset() {
presenter.onDestroyed();
presenter = null;
}
}

In this implementation Presenters should implement an interface that could look like:

public interface Presenter<V>{
void onViewAttached(V view);
void onViewDetached();
void onDestroyed();
}

The key elements in here are:

  • onStartLoading(): Will be called by the Framework for a new or already created Loader once Activity onStart() is reached. In here we check whether we hold a Presenter instance (— in which situation it will be delivered immediately) or the Presenter needs to be created.
  • onForceLoad(): Called when forceLoad() is invoked. Here we are calling the Factory to create our Presenter and delivering the result.
  • deliverResult(): will deliver our Presenter to the Activity/Fragment.
  • onReset(): will be call before the Loader gets destroyed, giving us the chance to communicate this to the Presenter in case some ongoing operation could be cancelled or additional clean ups would be required.
  • PresenterFactory: this interface will hide the details about how to create a Presenter when is required. We use this approach to favour composition over inheritance so we avoid the need of subclassing this PresenterLoader class to provide different Presenters. This interface could look as simple as:
public interface PresenterFactory<T extends Presenter> {
T create();
}

So how would it look from the Activity/Fragment point of view?

Ok, so now that we have our Loader implementation in place, we need to connects our activities and fragments. The connection point will be the LoaderManager. We will call FragmentActivity’s getSupportLoaderManager() or Fragment’s getLoaderManager() to get our instance and then call initLoader(). Google recommends calling this method in Activity#onCreate() or Fragment#onActivityCreated().

When calling initLoader we pass a unique id (unique within that Activity/Fragment — not globally unique) to identify the Loader, an optional Bundle — which in this case won’t be need, and an instance of LoaderCallbacks.
Note: As pointed out in the previous linked blog post do not call initLoader() within a Fragment’s onCreate() — always wait for onActivityCreated() or you’ll run into issues where Loaders appear to be shared across fragments as noted in this Lint request and this related Google+ post.
Note 2: (UPDATE: Following issue has been fix in Support Library 24.x.x onwards). Following this recommendation yields the following problem : onLoadFinished will be called twice during configuration changes. The situation is explained clearly in this stackoverflow post. Depending on what we do with the Presenter after being delivered it that can become an issue. In such case a possible workaround is calling initLoader in Fragment’s onResume. Another solution would be to keep the call in onActivityCreated and have some internal flag so we avoid delivering twice — You can check the latest solution in the linked code.

This call to initLoader will hook ourselves in the component lifecycle, receiving a call onStartLoading() when onStart() method is call and onStopLoading() when onStop(). However, onReset() will only be called if the Loader is destroy due to the Activity/Fragment being destroyed or because destroyLoader() has been called.

The LoaderManager includes a restartLoader() method which gives you the ability to force a reload. In our case it would not be necessary unless for some reason we wanted to manually recreate our Presenter.

But how do I get my Presenter? LoaderCallbacks

LoaderCallbacks is the communication point between Activity/Fragment and the Loader. There are three callbacks:

  • onCreateLoader() — where you construct the actual Loader instance.
  • onLoadFinished() — where Loader will deliver its work, the Presenter in our case.
  • onLoaderReset() — your chance to clean up any references to the data.

Show me the code

public class SampleActivity extends AppCompatActivity implements SomeView, LoaderManager.LoaderCallbacks<Presenter> {

private static final int LOADER_ID = 101;
private Presenter presenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// initialization code...

getSupportLoaderManager().initLoader(LOADER_ID, null, this);
}

@Override
protected void onStart() {
super.onStart();

// Ready to use presenter
presenter.onViewAttached(this);
}

@Override
protected void onStop() {
presenter.onViewDetached();
super.onStop();
}

// Some other activity code and view interface implementation...


@Override
public Loader<Presenter> onCreateLoader(int id, Bundle arg){
return new PresenterLoader<>(this, new SomeFactoryImpl());
}

@Override
public void onLoadFinished(Loader<Presenter> loader, Presenter presenter) {
this.presenter = presenter;
}

@Override
public void onLoaderReset(Loader<Presenter> loader) {
presenter = null;
}
}

The key elements in here are:

  • Init Loader with a unique ID within this instance of Activity/Fragment.
getSupportLoaderManager().initLoader(LOADER_ID, null, this);
  • Calling super.onStart will create or reconnect to an existing Loader and onStartLoading will be called. In our case this will create or deliver the Presenter instance in onLoadFinished. All of this will happen on the main UI thread, so by the time super.onStart is finished our Presenter will be ready to be used.
  • If the Loader needs to be created, the system will call onCreateLoader, where we will create our instance passing the proper PresenterFactory implementation.
    @Override
public Loader<Presenter> onCreateLoader(int id, Bundle arg){
return new PresenterLoader<>(this, new SomeFactoryImpl());
}

Et voila!

So summarizing a bit, we have seen that Loaders are a tool provided by the Android framework that offers the following:

  • They will be kept during configuration changes.
  • They are cleaned up by the system when the Activity/Fragment is no longer needed.
  • They are tied to the Activity/Fragment lifecycle, so this events could be propagated.
  • Each Activity/Fragment will have attached their own Loader instances, so several combination of “Presenter-Activity/Fragment” can be living at the same time, as in a ViewPager.
  • Loaders can run synchronously, making deterministic when its content will be deliver and ready to use.

So putting all this together and creating the proper abstractions, we have a good tool to “cache” our Presenters during orientation changes, making them survive as long as it is required.

You can check a sample project using this approach in different scenarios (MVP in an Activity, a Fragment or Fragment within a ViewPager) and the main constructs used during this post in this gist.

I would like to hear your thoughts in this approach.