ViewModel overview (Views)
Concepts and Jetpack Compose implementation
The ViewModel class is a business logic or screen level state
holder. It exposes state to the UI and encapsulates related business logic.
Its principal advantage is that it caches state and persists it through
configuration changes. This means that your UI doesn't have to fetch data again
when navigating between activities, or following configuration changes, such as
when rotating the screen.
ViewModel benefits
The alternative to a ViewModel is a plain class that holds the data you display in your UI. This can become a problem when navigating between activities or Navigation destinations. Doing so destroys that data if you don't store it using the saved instance state mechanism. ViewModel provides a convenient API for data persistence that resolves this issue.
The key benefits of the ViewModel class are essentially two:
- It lets you persist UI state.
- It provides access to business logic.
Scope
When you instantiate a ViewModel, you pass it an object that implements the
ViewModelStoreOwner interface. This may be a Navigation destination,
Navigation graph, activity, fragment, or any other type that implements the
interface. Your ViewModel is then scoped to the Lifecycle of the
ViewModelStoreOwner. It remains in memory until its ViewModelStoreOwner
goes away permanently.
A range of classes are either direct or indirect subclasses of the
ViewModelStoreOwner interface. The direct subclasses are
ComponentActivity, Fragment, and NavBackStackEntry.
For a full list of indirect subclasses, see the
ViewModelStoreOwner reference.
Implement a ViewModel
The following is an example implementation of a ViewModel for a screen that allows the user to roll dice.
Kotlin
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
// Expose screen UI state
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Handle business logic
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
Java
public class DiceUiState {
private final Integer firstDieValue;
private final Integer secondDieValue;
private final int numberOfRolls;
// ...
}
public class DiceRollViewModel extends ViewModel {
private final MutableLiveData<DiceUiState> uiState =
new MutableLiveData(new DiceUiState(null, null, 0));
public LiveData<DiceUiState> getUiState() {
return uiState;
}
public void rollDice() {
Random random = new Random();
uiState.setValue(
new DiceUiState(
random.nextInt(7) + 1,
random.nextInt(7) + 1,
uiState.getValue().getNumberOfRolls() + 1
)
);
}
}
You can then access the ViewModel from an activity as follows:
Kotlin
import androidx.activity.viewModels
class DiceRollActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same DiceRollViewModel instance created by the first activity.
// Use the 'by viewModels()' Kotlin property delegate
// from the activity-ktx artifact
val viewModel: DiceRollViewModel by viewModels()
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}
Java
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a ViewModel the first time the system calls an activity's onCreate() method.
// Re-created activities receive the same MyViewModel instance created by the first activity.
DiceRollViewModel model = new ViewModelProvider(this).get(DiceRollViewModel.class);
model.getUiState().observe(this, uiState -> {
// update UI
});
}
}
Recommended for you
- Note: link text is displayed when JavaScript is off
- Use Kotlin coroutines with lifecycle-aware components
- Save UI states
- Load and display paged data