Learning Android Development
Year 2020: Migrating from Activity to ViewModel
Time to move toward the Google preferred architecture for Android

For a span of 10 years, from 2007 to 2017, Android development revolved around Activity/Fragment as the heart of its development for each screen. Some developers tried to shift the logic out using MVP, MVVM, etc, but Google had yet to endorse anything back then.
In 2017, Architecture Component was introduced by Google. Android now has its own specialized ViewModel. However, it was still incomplete. It can’t save the state when the Activity is killed by Android OS. It still relies on Activity to provide the bundle to save and restore the state.
In 2019, SavedStateHandle was added to Android’s ViewModel. This now enables ViewModel capable of being the heart of Android Development for each screen.
So let’s dedicate this year, 2020, as a paradigm shift whereby each screen core development should be in ViewModel instead of Activity/Fragment.
Relook into Activity-Based Development
In order to demonstrate that the ViewModel is capable of replacing Activity/Fragment as the heart of each screen development, let’s look at the Activity-Based Development, using the below diagram.

From the diagram above, we could see that the Activity performs the following:
- Interacts with the XML View to perform UI-related activities
- Get informed on Lifecycle event changes from Android OS
- Save and restore state, if the activity is killed by the Android OS
- Handling dependencies Service e.g. Repository
As you could see, Activity is pretty much the center of the screen development since it is aware of many things and need to handle all of them.
The challenges posed by Activity/Fragment-Based Development
Having Activity/Fragment as central of development for the screen post several issues as below.
- Activity/Fragment can’t be easily injected with Dependencies. That’s one of the reasons Dagger was made so popular.
- Activity/Fragment is not easily testable with Unit Test. Robolectric was introduced. However, it’s a very painful framework to work, on and yet to be endorsed by Google after so many years.
- Activity/Fragment is still pretty much a VIEW in many regards. It is not an ideal place for any business logic.
- Interim data has to be explicitly restored/saved using SavedStateInstance. This can only happen on a special lifecycle event i.e. onCreate and onSaveInstanceState only.
In knowing such disadvantages, the developer community recommends MVP (and later MVVM) as the workaround. This enabled us to move the logic part from the Activity to the Presenter, as well as the dependent services. However, the other parts of Activity/Fragment responsibilities (e.g. response to lifecycle event, state persistency) are still pretty much an Activity/Fragment controlled area. Hence making the developer made MVP and MVVM a half-baked solution.
Architecture Component ViewModel: the new core
This is 2020. The Android ViewModel is now fully equipped to alleviate the Activity/Fragment from other nonessential activities. It can now be the core.
Below is the exact replica Activity class logic (in the diagram above) but implemented in ViewModel instead. Based on the diagram below, let’s look into how each is handled.

1. Interacting with the View (Activity/Fragment)
Instead of interacting with the XML view directly, the ViewModel interacts with Activity/Fragment. This leaves Activity/Fragment to continue to handle XML as that’s what it’s should be responsible for (e.g. hiding/showing view, animating, etc)
To interact with the Activity/Fragment, the ViewModel instantiates some LiveData.
val showTextDataNotifier: LiveData<String>
get() = showTextLiveDataOnce the LiveData value is updated, it will broadcast to the Activity/Fragment that is observing it, and perform the needed UI update.
In the Activity, we just need to instantiate observer, with the action, it wants to perform upon receiving the signal,
private val textDataObserver =
Observer<String> { data -> text_view.text = data }then register the observer with the liveData from the ViewModel as below.
viewModel.showTextDataNotifier.observe(
this /*activity*/, textDataObserver)LiveData is a great data communicator, as it is aware of the Activity/Fragment lifecycle before interacting with it. To get a good understanding of LiveData, check out
2. Get Lifecycle Events to respond
Lifecycle continues to be something that is made known to Activity/Fragment. However, on top of that, the ViewModel could be aware of the Activity/Fragment’s Lifecycle by implementing LifecycleObserver
class MyViewModel(/*...*/) : ViewModel(), LifecycleObserver {After that, we just annotate a function with @OnLifecyecleEvent together with the intended Lifecycle enum (e.g. ON_PAUSE below).
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun anyNameFunction() {
// Do what one does when onPause
}In the Activity, it just needs to add the ViewModel as an observer to the lifecycle registry using the addObserver API.
lifecycle.addObserver(viewModel)This makes the ViewModel capable of knowing all the essential lifecycle, i.e. onCreate, onStart, onResume, onPause, onStop, onDestroy, and also onAny of them, if it has any function that annotates the enum.
3. Persist state when ViewModel is destroyed by Android OS
Before 2019, this is one of the missing bits that makes the Architecture ViewModel solution incomplete. Back then, it does have the ability to retain state onConfigurationChanged but not when it is killed by the Android OS.
Gratefully in 2019, Google came up with the savedStateHandle that solves this problem.
To get the savedStateHandle, one just needs to add it as a parameter. It will be provided (almost) AUTOMATICALLY!
class MyViewModel(
private val savedStateHandle: SavedStateHandle) :
ViewModel()With it, one could just set and get with the KEY accordingly. It essentially could replace what savedInstanceState is done in Activity/Fragment, and more as below
- In the ViewModel, one could access to
savedStateHandleany time, and hence the save and retrieve could be performed any time as needed instead of just on a particular lifecycle event. - Other than storing a normal primitive and serializable object, it also allows us to store LiveData! The nice bit about storing Livedata is, one just needs to get the Livedata from
SavedStateHandleas below, and then any value updated to the LiveData would be automatically stored in thesavedStateHandle.
private val showTextLiveData
= savedStateHandle.getLiveData<String>(KEY)To read more about saving/restoring LiveData in SavedStateHandle, read
4. Handling dependencies Service e.g. Repository
Unlike Activity/Fragment, the ViewModel provides the flexibility of one to inject the Dependencies through the Constructor.
For example, we could inject repository as below, which is something we can’t do in Activity/Fragment.
class MyViewModel(
private val savedStateHandle: SavedStateHandle,
private val repository: Repository) :
ViewModel(), LifecycleObserverHowever, unlike conventional developer-created MVP or MVVM, it also has savedStateHandle. The ViewModel doesn’t really get manually instantiated, so injecting the dependencies into it is not something one could do directly.
If the ViewModel created doesn’t depend on any services (e.g. Repository), we would create the ViewModel with the ViewModels delegate within the Activity. This will create the ViewModel with savedStateHandle automatically injecting it into it.
private val viewModel: MyViewModel by viewModels()However, if the ViewModel is dependent on some Services (e.g. Repository), we would first need to create a factory for the ViewModel, as below, with the parameter for the dependencies to be injected.
class MyViewModelFactory(
owner: SavedStateRegistryOwner,
private val repository: Repository,
defaultArgs: Bundle? = null
) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
override fun <T : ViewModel> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return MyViewModel(handle, repository) as T
}
}then we could then create the ViewModel in the Activity using the Factory.
private val viewModel: MyViewModel by viewModels{
MyViewModelFactory(this, Repository())
}Notice in the code above, it has 2 things to inject into the ViewModel factory, ie. the this (which is the SavedStateRegistryOwner, which is also the Activity itself), and the Repository. So this means the Activity needs to have the Repository as well.
The need of having the Repository in activity is a pain point as we need to inject the Repository into Activity, which is not something we could do through constructor for now.
There’s some way to use Dagger (or other approaches e.g. Koin, Kodein) to help with the injection for now. You could refer to the below blog on how to inject through Dagger 2.
5. Bonus: Unit Testing ViewModel
The ability to easily unit test the ViewModel itself is already enough reason we should move all core development of screen from Activity to ViewModel as much.
To perform the needed Unit Test, we just need to add InstantTaskExecutorRule() as below
class MyViewModelTest {@get:Rule
var rule: TestRule = InstantTaskExecutorRule()@Test
fun `my testing`() {
// All testing.
}Using Mockk, I could mock the SavedStateHandle and even LiveData if I needed to. Below is one example where I mock everything the ViewModel is dependent on.
@Test
fun `test init with default value`() {
val savedStateHandle = mockk<SavedStateHandle>()
val repository = mockk<Repository>()
val liveData = mockk<MutableLiveData<String>>()every { liveData.value } returns "Default Value"
every { savedStateHandle.getLiveData<String>(KEY) }
returns liveData val viewModel = MyViewModel(savedStateHandle, repository)verify { savedStateHandle.getLiveData<String>(KEY) }
Assert.assertEquals(viewModel.showTextDataNotifier.value,
"Default Value")
}Having the 4 needing responsibilities sits in ViewModel, the ViewModel is now the new Activity. The core of development could now reside in ViewModel while letting the Activity/Fragment handles the UI-specific tasks. Maybe the ViewModel should be named Activity instead 😅
The ViewModel in now the new Activity
I think the next thing Google might be tackling is the ability to automatically inject the Dependencies into the ViewModel, as it is the sole pain point the ViewModel has today. Check out the below.
The above example code with the unit test on ViewModel is in
Thanks for reading. You can check out my other topics here.
Follow me on medium, Twitter, Facebook or Reddit for little tips and learning on mobile development etc related topics. ~Elye~






