A journey to the world of MVP and Loaders ( Part 2)
Example project for this post is at this link. The master branch explores both MVP and Loader API while the other only-mvp branch only explores MVP as explained in Part 1.
Loader API and the rotation problem
The rotation problem is that we fetched some data by a costly operation like a network call or a complex query with transformation from a database. Then the user rotated the phone and we don’t want to do the costly query again. Here comes in the Loader API. Loader API was introduced in API level 11 and is used to load data from a data source to display in an activity or fragment. It has many advantages and solves many problems but the most useful one to us regarding rotation is that Loaders are managed by the system and they persist during configuration changes. According to the documentation of Loaders:
Loaders persist and cache results across configuration changes to prevent duplicate queries.
So if we make the loader load presenter in the first creation of our activity, and then rotate the phone which will recreate the whole activity, we will not make a duplicate query and get back the same presenter as pre-rotation. And then using the cached data in the presenter we can persist our state or instance of data across rotation. Now lets dive into the code.
Loader API has three major classes and interfaces:
1. LoaderManager: Responsible for managing loaders for an activity or fragment. The system automatically determines whether a loader with the same integer ID already exists and will either create a new loader or reuse an existing loader. So in case of rotation, it reuses the same loader giving us our pre-rotation presenter object back.
2. LoaderCallbacks: Interface required to be implemented by the activity or fragment to get the callback from when loader events occur. Although there are three functions, we mostly need to focus on two for our rotation case.
- onCreateLoader(int, Bundle): Callback for when the system needs a new Loader and thus is asking for us to create one.
- onLoadFinished(Loader<D>, D): Callback for when the load has been finished by the loader and we get back the data as ‘D’.
- onLoaderReset: Callback for when a previously created loader is being reset. We won’t need to focus on this though for our rotation use case.
3. Loader: This is the abstract class which serves as the basis for all loader classes.
Implementation of loader API
Lets start with defining our custom loader class. So we need to extend the abstract Loader class. We will have a private member, our presenter we want to load, and will override one function: onStartLoading(). It is normally called as soon as our activity or fragment gets started. As our result doesn’t take time to load and is created when the loader was being created, we will deliver it as soon as this function is called.
In our activity, we will start the initialization of the Loader in onCreate(). If this is the first time activity is being created a new loader will be created by the system by calling onCreateLoader() callback.
And the loader callbacks are implemented in an activity or fragment as shown below:
In this way we can persist our presenter during rotation, in turn persisting our data and any kind of database or network request we were making.
Please note that there are many alternatives to Loader API to persist presenters and many other uses of Loader API. The documentation on Loader API is pretty good, also for more understanding you can read this gold article by Ian Lake.
Please check out the example project for full code implementation.
And I would love to discuss your thoughts on this. If you like the article, hit that green heart button.