Android MVP that survives view life-cycle, configuration & internet changes, part-1
MVP + Clean architecture + RxJava, a combination which has successfully proven the test of time and its here to stay for a while. If you are already using this approach or planning to adapt it soon, here is the question worth asking, How to design an MVP that,
- Survives life-cycle changes of the view
- Survives configuration changes, part-2
- Survives internet connection changes, part-2
I came across multiple ways to solve these issues. After some experiments and discussions, I finalised a combination of concepts which collectively solves these survival problems for my requirements. Here is the flow chart which explains the overall picture, which we will discuss in detail.
In MVP, Presenter is directly dependent on the View’s life cycle and Presenter directly controls the life cycle of the Data source. This means that the Data source’s life cycle is also dependent on the View’s life cycle. Since, Data source handles heavy network calls and database operations, if we don’t handle this life cycle issue properly, a single change in View’s life cycle could end up in a heavy network call.
To solve this issue, one of the possible solution, is to create a two layer subscription system. Eugenio Marletti, thanks for the discussion.
View subscription, that is between View and Presenter
Data subscription, that is between Presenter and Data source
This two layer subscription system, makes sure that a life cycle change in the view, won’t trigger an unnecessary network/database call on the data source. Now that we have two separate subscriptions, we can independently subscribe or unsubscribe the subscriptions based on our requirements.
Question is: When should we subscribe & unsubscribe view subscription?
Solution 1: Subscribe on onResume() & unsubscribe on onPause()
Solution 2: Subscribe on onStart() & unsubscribe on onStop()
Problem: View reloads when it shouldn’t
onPause() gets called when activity/fragment is partially not visible to user, and onStop() gets called when the activity/fragment is fully not visible to user. Which makes them a perfect callback to unsubscribe view subscription, but the problem with onPause() and onStop() is that, it also gets called for the cases like, switch between applications, navigation to a different view, configuration changes, lock screen, popup messages etc.
Since, we have untangled the data subscription from the view subscription, we have already avoided making a fresh data call, but we still have to load the view with cached data, even though it might not be heavy processing, we can not ignore it. Also, not to forget the loading animation user might see for a short period of time, because we are loading the view every time.
Note: If you have to use any of the 2 solutions, because of the requirements, both the above listed issues can be handled using a few flags.
Solution 3: Subscribe on onCreate() & unsubscribe on onDestroy()
In the part-2 of this post we will discuss that we need to use Fragment + setRetainInstance(true) to handle configuration changes. First problem here is that by the time onCreate() gets called the view is not created, which will lead to a null pointer exception. Also, if we use setRetainInstance(true) on Fragment, onCreate() and onDestroy() will not get called, and thus we will not be able to update the view with updated configuration.
Note: If you decide to use a different approach for configuration changes, onCreate() and onDestroy() combination can also be used.
Solution: Subscribe on onViewCreated() & unsubscribe on onDestroyView()
For this solution, we need to use a Fragment for our view, which will also be useful when it comes to configuration change survival. Thanks Carmelo iriti.
The advantage with this approach is that we won’t have to reload the view or reestablish the view subscription as long as the view is alive, except for the configuration changes, but when it comes to configuration change, view reloading is expected by user and should not be a problem.
Note: This approach works only with Fragments.
Now that we have addressed the view subscription life cycle, lets focus on the data subscription life cycle.
When should we subscribe & unsubscribe data subscription?
Data subscription life cycle directly deals with the heavy calls and operations, which means that it should survive as long as it can, and that is why we should use the following solution,
Subscribe on onViewCreated() & unsubscribe on onDestroy()
With this approach, whenever we first create a view subscription, we also create a data subscription, and in-between even if the view subscription is unsubscribed because of configuration changes, data subscription will survive until the view is destroyed.
Lets see some code, here is how the Fragment as View looks like,
Here is how the Presenter looks like,
Here is how the Data source looks like,
Use 2 separate subscriptions view and data subscription. subscribe for view and data subscription on onViewCreated(), unsubscribe view subscription on onDestroyView() and unsubscribe data subscription on onDestroy().
Complete Source Code
Complete source code is available on GitHub.
A set of use case specific code examples are available Here.