ViewModel – Übersicht (Views)

Konzepte und Jetpack Compose-Implementierung

Die Klasse ViewModel ist ein State Holder auf Geschäftslogik- oder Bildschirmebene. Sie stellt den Status für die Benutzeroberfläche bereit und kapselt die zugehörige Geschäftslogik. Der Hauptvorteil besteht darin, dass der Status im Cache gespeichert und bei Konfigurationsänderungen beibehalten wird. Das bedeutet, dass Ihre Benutzeroberfläche beim Wechseln zwischen Aktivitäten oder nach Konfigurationsänderungen, z. B. beim Drehen des Bildschirms, keine Daten mehr abrufen muss.

Vorteile von ViewModels

Die Alternative zu einem ViewModel ist eine einfache Klasse, die die Daten enthält, die Sie in Ihrer Benutzeroberfläche anzeigen. Das kann zu Problemen führen, wenn Sie zwischen Aktivitäten oder Navigationszielen wechseln. Dadurch werden diese Daten zerstört, wenn Sie sie nicht mit dem Mechanismus für den gespeicherten Instanzstatus speichern. ViewModel bietet eine praktische API für die Datenpersistenz, mit der dieses Problem behoben wird.

Die wichtigsten Vorteile der ViewModel-Klasse sind im Wesentlichen zwei:

  • Damit können Sie den UI-Status beibehalten.
  • Sie bietet Zugriff auf die Geschäftslogik.

Umfang

Wenn Sie ein ViewModel instanziieren, übergeben Sie ihm ein Objekt, das die ViewModelStoreOwner-Schnittstelle implementiert. Das kann ein Navigationsziel, ein Navigationsdiagramm, eine Aktivität, ein Fragment oder ein anderer Typ sein, der die Schnittstelle implementiert. Ihr ViewModel ist dann auf den Lifecycle des ViewModelStoreOwner beschränkt. Sie bleibt im Arbeitsspeicher, bis die ViewModelStoreOwner endgültig entfernt wird.

Eine Reihe von Klassen sind entweder direkte oder indirekte Unterklassen der ViewModelStoreOwner-Schnittstelle. Die direkten Unterklassen sind ComponentActivity, Fragment und NavBackStackEntry. Eine vollständige Liste der indirekten Unterklassen finden Sie in der ViewModelStoreOwner-Referenz.

ViewModel implementieren

Das Folgende ist ein Beispiel für die Implementierung eines ViewModel für einen Bildschirm, auf dem der Nutzer würfeln kann.

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

Sie können dann so auf das ViewModel aus einer Aktivität zugreifen:

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