Clean MVVM Activity Lifecycle

The missing lifecycle diagram for reactive Android Apps

Eric Silverberg
Perry Street Software Engineering
5 min readFeb 26, 2021

--

How Activities, Repositories and Databases fit together on Android. Photo by Bill Oxford on Unsplash

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:

Behold — the missing 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 RxJavaDisposables 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

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.

--

--