ViewModel
Objective
- The starter app doesn’t save and restore the app state during configuration changes, such as when the device orientation changes, or when the app shuts down and restarts.
onSaveInstanceState()
requires us to add extra codes to save and retrieve the state from a bundle- We should keep the size of data in a bundle as minimal as possible. (Limitation on the size of a bundle, 500KB to 1MB)
- The game screen does not navigate to the score screen when the user taps the End Game button.
How to solve?
-
We can make it by using the recommended App architecture. This diagram shows how all the modules should interact with one another after designing the app.
-
This codelab follows the separation of concern design principle, dividing classes into sections addressing distinctive concerns.
- UI with Activity / Fragment
- ViewModel and Repository <- current scope
- Model and Data sources
Overview of ViewModel, ViewModelProvider, and ViewModelFactory
-
Definition of ViewModel
The
ViewModel
class is designed to store and manage UI-related data in a lifecycle conscious way. TheViewModel
class allows data to survive configuration changes such as screen rotations.A
ViewModel
can do simple calculations and transformations on data to prepare the data to be displayed by the UI controller. In this architecture, theViewModel
performs the decision-making, and UI component works according to the data transformed in the ViewModel.implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
class GameViewModel : ViewModel() { init { Log.i("GameViewModel", "GameViewModel created!") } override fun onCleared() { super.onCleared() Log.i("GameViewModel", "GameViewModel destroyed!") } }
-
Definition of ViewModelProvider
During configuration changes such as screen rotations, UI controllers such as fragments are re-created. However,
ViewModel
instances survive. If you create theViewModel
instance using theViewModel
class, a new object is created every time the fragment is re-created. Instead, create theViewModel
instance using aViewModelProvider
.Important: Always use
ViewModelProvider
to createViewModel
objects rather than directly instantiating an instance ofViewModel
.private lateinit var viewModel: GameViewModel viewModel = ViewModelProvider(this).get(GameViewModel::class.java)
-
ViewModelProvider
returns an existingViewModel
, otherwise creates a new one. -
ViewModelProvider
creates aViewModel
instance in the given scope (Activity
orFragment
).
-
-
Definition of ViewModelFactory
A
ViewModelFactory
instantiatesViewModel
objects, with or without constructor parameters.In this example, you want a
ViewModel
to hold the score, and you will pas in the score value during theViewModel
initialization.Factory method pattern that uses factory methods to create objects.
class ScoreViewModel(finalScore: Int) : ViewModel() { // The final score var score = finalScore init { Log.i("ScoreViewModel", "Final score is $finalScore") } }
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory { override fun <T : ViewModel?> create(modelClass: Class<T>): T { if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) { return ScoreViewModel(finalScore) as T } throw IllegalArgumentException("Unknown ViewModel class") } }
// code for sending score, which is put into ScoreViewModel as a parameter val action = GameFragmentDirections.actionGameToScore() action.score = viewModel.score NavHostFragment.findNavController(this).navigate(action) }
private lateinit var viewModel: ScoreViewModel private lateinit var viewModelFactory: ScoreViewModelFactory viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score) viewModel = ViewModelProvider(this, viewModelFactory).get(ScoreViewModel::class.java)
Homework
- To avoid losing data during a device-configuration change, you should save app data in which class?
ViewModel
LiveData
Fragment
Activity
- A
ViewModel
should never contain any references to fragments, activities, or views. True or false?- True
- False
- When is a
ViewModel
destroyed?- When the associated UI controller is destroyed and recreated during a device-orientation change.
- In an orientation change.
- When the associated UI controller is finished (if it’s an activity) or detached (if it’s a fragment).
- When the user presses the Back button.
- What is the
ViewModelFactory
interface for?- Instantiating a
ViewModel
object. - Retaining data during orientation changes.
- Refreshing the data being displayed on the screen.
- Receiving notifications when the app data is changed.
- Instantiating a