Descripción general de ViewModel (vistas)

Conceptos y la implementación de Jetpack Compose

La clase ViewModel es un contenedor de lógica empresarial o estado a nivel de pantalla. Expone el estado a la IU y encapsula la lógica empresarial relacionada. Su principal ventaja es que almacena en caché el estado y lo conserva durante los cambios de configuración. Esto significa que la IU no tiene que recuperar datos cuando navegas entre actividades o si sigues cambios de configuración, como cuando rotas la pantalla.

Beneficios de ViewModel

La alternativa a un ViewModel es una clase simple que contiene los datos que muestras en tu IU. Esto puede convertirse en un problema cuando navegas entre actividades o destinos de Navigation. Si lo haces, esos datos se destruirán si no los almacenas con el mecanismo de guardado de estado de instancias. ViewModel proporciona una API conveniente para la persistencia de datos que resuelve este problema.

Los beneficios clave de la clase ViewModel son básicamente dos:

  • Te permite conservar el estado de la IU.
  • Proporciona acceso a la lógica empresarial.

Alcance

Cuando creas una instancia de ViewModel, le pasas un objeto que implementa la interfaz ViewModelStoreOwner. Puede ser un destino de Navigation, un gráfico de navegación, una actividad, un fragmento o cualquier otro tipo que implemente la interfaz. El alcance de tu ViewModel se define en el Lifecycle del ViewModelStoreOwner. Continúa en la memoria hasta que su ViewModelStoreOwner desaparece de forma permanente.

Un rango de clases son subclases directas o indirectas de la interfaz ViewModelStoreOwner. Las subclases directas son ComponentActivity, Fragment y NavBackStackEntry. Para obtener una lista completa de las subclases indirectas, consulta la ViewModelStoreOwner referencia.

Cómo implementar un ViewModel

El siguiente es un ejemplo de implementación de un ViewModel para una pantalla que le permite al usuario lanzar un dado.

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
            )
        );
    }
}

Luego, puedes acceder a ViewModel desde una actividad de la siguiente manera:

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
        });
    }
}