Panoramica di ViewModel (visualizzazioni)

Concetti e implementazione di Jetpack Compose

La classe ViewModel è un contenitore di stato a livello di logica di business o schermata. Espone lo stato all'UI e incapsula la logica di business correlata. Il suo vantaggio principale è che memorizza nella cache lo stato e lo mantiene durante le modifiche alla configurazione. Ciò significa che l'UI non deve recuperare di nuovo i dati quando si passa da un'attività all'altra o in seguito a modifiche alla configurazione, ad esempio quando si ruota lo schermo.

Vantaggi di ViewModel

L'alternativa a un ViewModel è una classe semplice che contiene i dati visualizzati nell'UI. Questo può diventare un problema quando si passa da un'attività all'altra o da una destinazione di navigazione all'altra. In questo caso, i dati vengono eliminati se non li memorizzi utilizzando il meccanismo di stato dell'istanza salvata. ViewModel fornisce una comoda API per la persistenza dei dati che risolve questo problema.

I principali vantaggi della classe ViewModel sono essenzialmente due:

  • Consente di rendere persistente lo stato dell'UI.
  • Fornisce l'accesso alla logica di business.

Ambito

Quando crei un'istanza di ViewModel, gli passi un oggetto che implementa l'interfaccia ViewModelStoreOwner. Può trattarsi di una destinazione di navigazione, di un grafico di navigazione, di un'attività, di un fragment o di qualsiasi altro tipo che implementa l'interfaccia. Il ViewModel viene quindi definito nell'ambito del Lifecycle del ViewModelStoreOwner. Rimane in memoria finché il relativo ViewModelStoreOwner non viene eliminato definitivamente.

Una serie di classi sono sottoclassi dirette o indirette dell'interfaccia ViewModelStoreOwner. Le sottoclassi dirette sono ComponentActivity, Fragment e NavBackStackEntry. Per un elenco completo delle sottoclassi indirette, consulta il ViewModelStoreOwner riferimento.

Implementare un ViewModel

Di seguito è riportato un esempio di implementazione di un ViewModel per una schermata che consente all'utente di tirare i dadi.

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

Puoi quindi accedere al ViewModel da un'attività nel seguente modo:

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