ViewModel and LiveData in practice
Thursday, March 22, 2018For years, Android had no official answer to "how do you survive rotation". You use onSaveInstanceState for small data, a retained Fragment for larger objects, static references if you are not careful about leaks. Everyone has a different solution.
At I/O 2017 Google announces Architecture Components. ViewModel and LiveData. Now there is an official way.
ViewModel
A ViewModel survives configuration changes. When the Activity rotates, Android destroys and recreates it. The ViewModel stays.
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
fun loadUser(id: String) {
// this runs in the ViewModel, survives rotation
repository.getUser(id) { user ->
_user.postValue(user)
}
}
}
In the Activity:
val viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
viewModel.loadUser(userId)
After rotation, ViewModelProvider returns the same ViewModel instance. The network call is not repeated. The data is already there.
LiveData
LiveData is an observable that is lifecycle-aware. It only delivers updates when the observer (Activity or Fragment) is in an active state. If the Activity is in the background, updates are queued until it comes back.
viewModel.user.observe(this) { user ->
nameTextView.text = user.name
emailTextView.text = user.email
}
No need to unsubscribe. When the Activity is destroyed, the observer is automatically removed. No memory leak.
Before LiveData, you had to manually unsubscribe from RxJava streams or callbacks in onDestroy. Easy to forget, causes crashes.
The MutableLiveData pattern
Inside the ViewModel, you use MutableLiveData to post updates. Outside, you expose it as LiveData so only the ViewModel can change the value.
// inside ViewModel
private val _loading = MutableLiveData<Boolean>()
val loading: LiveData<Boolean> = _loading
fun fetchData() {
_loading.value = true
// do work
_loading.value = false
}
The underscore prefix for the mutable version is a convention that almost everyone follows.
What changes in the codebase
We start moving all the state and logic out of Activities into ViewModels. Activities become thinner. They observe LiveData and update the UI. Business logic lives in the ViewModel.
Combined with a Repository pattern, the architecture becomes clean:
Activity/Fragment -> ViewModel -> Repository -> API/Database
This is not new. MVP had a similar idea. But now it is official, it handles rotation for you, and it integrates with LiveData natively.
The one thing I miss
ViewModel does not survive process death. If Android kills the process and the user comes back, the ViewModel is recreated. You still need onSaveInstanceState for critical state like search query or scroll position.
This is a real limitation. The combination of ViewModel for configuration changes and onSaveInstanceState for process death is correct but adds complexity. Something to be aware of.
Still, Architecture Components are a big improvement. The codebase becomes more predictable.