Advanced ViewModels (part IV): Persisting state across process recreation.

Lucas Nobile
4 min readSep 15, 2020

--

ViewModel as the bridge between the View and the Model.

TL;DR: We can pass parameters to our ViewModel, use it as a data holder, also to share data between Fragments, and to persist its state across process recreation.

If our app is put in the background by the System, for example in the situation the device is running low on memory or a call is incoming, its process might be killed. If the process is killed, the ViewModel is destroyed as well.

We can simulate the system killing the process (requires an emulator running P+). First make sure the process is running by typing:

$ adb shell ps -A |grep <app package name>

This should output the running process with name: <app package name>

Then, we press Home on our device or emulator and then run:

$ adb shell am kill <app package name>

If we then type again:

$ adb shell ps -A |grep <app package name>

We should get nothing, indicating that the process has been killed correctly.

Now, we need a mechanism to store information regarding the UI’s state, so whenever we come back to the app we find it in the same state we had left it in.

With the Saved State module, our ViewModel can handle saving and restoring data.

Per documentation, our ViewModel receives a SavedStateHandle object via its constructor. This is a key-value map that will let us write and retrieve objects to and from the saved state. These values will persist after the process is killed by the System and remain available via the same object.

⚠️ State must be simple and lightweight because is a Bundle.

The SavedStateHandle class has the methods you expect for a key-value map:

get(String key)
contains(String key)
remove(String key)
set(String key, T value)
keys()

Also, there is a special method: getLiveData(String key) that returns the value wrapped in a LiveData observable.

So, now our ViewModel will receive a SavedStateHandle object in its constructor:

class SharedNewsViewModel(
private val savedStateHandle: SavedStateHandle,
private val newsService: NewsService
) : ViewModel()

And the Factory for our ViewModel now needs to extend from AbstractSavedStateViewModelFactory with a SavedStateRegistryOwner and Bundle as default state:

class Factory(
owner: SavedStateRegistryOwner,
defaultState: Bundle?
,
private val newsService: NewsService
) : AbstractSavedStateViewModelFactory(owner, defaultState) {
override fun <T : ViewModel?> create(
key: String,
modelClass: Class<T>,
handle: SavedStateHandle
): T {
return SharedNewsViewModel(handle, newsService) as T
}
}

Now, we need to define what we are going to save as the ViewModel state.

In our case we will be saving the query the user entered to search for news.

So, in our ViewModel we will have the key, remember the State is a Bundle.

companion object {
private const val QUERY_KEY = "query"
}

And we will have the getter and setter for that state:

fun getQuery() = savedStateHandle.get<String>(QUERY_KEY)private fun saveQuery(query: String) = savedStateHandle.set(QUERY_KEY, query)

So, in our ViewModel, when there is a new call to search for news we are going to save that query:

fun searchNews(query: String) {
saveQuery(query)
...
}

Then, in our View we will be creating the ViewModel this way:

private val viewModel: SharedNewsViewModel by activityViewModels { SharedNewsViewModel.Factory(this, null, newsService) }

We are passing this as the owner, null as a default state, and the service.

Finally, the pattern will be:

private fun getNews() {
viewModel.getNews()?.let { news ->
// Get news from LiveData
showNews(news)
} ?: run {
viewModel.getQuery()?.let { query ->
// Get saved query from ViewModel state
searchNews(query)
} ?: run {
// Show empty view
binding.tvEmpty.show()
}
}
}

Let me share with you all the repo:

And my tech talk about Advanced ViewModels on Android for GDG Córdoba, Argentina (in Spanish):

Conclusion

ViewModel is the core part of Android MVVM and we need to master it. Here we have some tips to elevate our ViewModel to the next level by passing parameters to it, by using it as a data holder, also how we can share a ViewModel between Fragments, and to persist its state across process recreation.

Thanks for reading and start using advanced ViewModels today 🙌

--

--