Clean MVVM Activity Lifecycle
The missing lifecycle diagram for reactive Android Apps
Every Android developer working with an MVVM architecture faces the choice of how and when to configure views and subscriptions. Do this incorrectly and you will experience seemingly impossible NullPointerExceptions, as we documented earlier in this series.
To avoid these consequences, simplify your code and streamline your architecture, our company has developed its own proposed Activity and Fragment lifecycle that we have dubbed the Clean MVVM Activity Lifecycle:
This lifecycle lives alongside the standard Android Activity lifecycle, with its own hooks in onCreate()
and onResume()
.
As an app developer, there are two basic functions we nearly always want to complete, and one basic event that we want to hear:
- Setup and customize Views
- Subscribe to event publishers, whether they be RxJava or LiveData
- Know when the setup process is 100% completed
The methods we describe below give us these hooks.
Method walk-through
Let’s go through each of these methods and explain their responsibilities:
onCreate()
Historically this method would be a dumping ground for developers who want to configure an Activity. According to the Google docs, “This is where most initialization should go.”
Google is wrong — no imperative initialization should be done in
onCreate()
.
This method is a signal to start a separate, asynchronous and idempotent initialization process — NOT the actual “doer” of initialization.
onSetupViews()
This is the first of custom-implemented Clean MVVM lifecycle methods.
onSetupViews()
will perform the work of setting up our views, adapters, the toolbar, etc. We’ll also set up our click listeners that will forward the user actions to the ViewModel. In most cases, this method will, in fact, be called synchronously in onCreate()
except for the case where our repositories are not warm (see Android process death). In that case, onSetupViews()
will be called only after our repositories have finished the initialization.
We also check here whether the Activity is finishing or is already destroyed before setting up the views. If onSetupViews()
gets called asynchronously, then it’s possible for the Activity to have been destroyed in the meantime.
onSetupLiveDataEventSubscriptions()
Here we’ll set up our LiveData
subscriptions which in most cases is the state change subscription that is stored in the ViewModel (more about what a state change subscription means in our earlier series on Clean MVVM and ViewModels). The same rule applies here as well — if our repositories are not warm, we’ll wait for them first to initialize.
onSetupAliveActivityRxJavaEventSubscriptions()
In this method, we’ll set up the list of RxJavaDisposable
s which we want to persist even when the app is backgrounded. In other words, any subscription that we want to stay alive until the Activity is destroyed will be put here. In most cases, any events we observe from the ViewModel (RxJava
streams) need to be set up here.
onSetupVisibleActivityRxJavaEventSubscriptions()
This is similar to the method above, with the difference that these subscriptions will remain alive only while our app is visible because they are started in onResume()
and they are destroyed in onPause()
. That means that they will be automatically closed once the app moves to the background but they will be set up again once the app is foregrounded again.
For example, it makes sense to include here streams that the Activity is observing from the adapter of a RecyclerView
(i.e., click events in items of the RecyclerView
), because explicit user actions cannot be performed if the app is in the background.
onInitializationComplete()
It’s guaranteed that this method will be called only after our repositories have finished initialization, our views and subscriptions have been set up, and the Activity or Fragment is visible (i.e. the app is not backgrounded). Any post-setup operation can be put here, such as handling a deep link or a notification intent.
onStart() / onResume()
By using the methods that we described above, we have realized that there’s no need to have any code in these two methods. We never access views directly in onStart()
or onResume()
because now that onSetupViews()
is asynchronous, there’s no guarantee that the views will have been initialized at this point.
What are you waiting for? Profiles, for starters.
In this architecture, we talk about “waiting for initialization logic,” but what does that mean in practice?
In our app, the AccountRepository
contains the currently signed-in user’s Profile
which is loaded from the database on initialization and stored in memory as an Observable
. Though we publish dating apps, it is safe to assume that most apps will also require a similar structure. Whatever app you are building, you will likely need an application-scoped repository that contains the current profile, with reactive methods that indicate when it has been loaded from disk.
A note about Fragments
We’ve talked about Activity lifecycles and although the Fragment has slightly different lifecycle callbacks, the same rules and patterns apply there as well.
Next Up
We referenced repository initialization, but now it’s time to dive into our particular implementation of asynchronous, idempotent initialization.
Further reading
- Android Activity Lifecycle considered harmful
- Android process death
- Clean MVVM Activity Lifecycle ← you are here
- Repository Initialization
- Activity Lifecycle Cheat Sheet
- Debugging Android process death
One more thing…
If you’re a mobile developer who enjoys app architecture, are interested in working for a values-first app company serving the queer community, talk to us! Visit https://www.scruff.com/careers for info about jobs at Perry Street Software
Other series you might like
Clean API Architecture (2021)
Classes, execution patterns, and abstractions when building a modern API endpoint.
Kotlin in Xcode? Swift in Android Studio? (2020)
A series on using Clean + MVVM for consistent architecture on iOS & Android
About the authors
Eric Silverberg and Stelios Frantzeskakis are developers for Perry Street Software, publishers of the LGBTQ+ dating apps SCRUFF and Jack’d, with more than 20M members worldwide.