ViewModel – Übersicht   Teil von Android Jetpack.

Mit Kotlin Multiplatform testen
Mit Kotlin Multiplatform kann die Geschäftslogik mit anderen Plattformen geteilt werden. Informationen zum Einrichten und Verwenden von ViewModel in KMP

Die ViewModel Klasse 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 die Benutzeroberfläche beim Wechsel zwischen Aktivitäten oder nach Konfigurationsänderungen, z. B. beim Drehen des Bildschirms, keine Daten noch einmal abrufen muss.

Weitere Informationen zu Status-Holdern finden Sie in der Anleitung zu Status-Holdern. Weitere Informationen zur Benutzeroberfläche finden Sie in der Anleitung zur Benutzeroberfläche.

Vorteile von ViewModel

Die Alternative zu einem ViewModel ist eine einfache Klasse, die die Daten enthält, die Sie auf der Benutzeroberfläche anzeigen. Dies kann beim Wechsel zwischen Aktivitäten oder Navigationszielen zu Problemen führen. Dabei werden die 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.

Alternativ bietet Compose für reine Status-Holder retain-Funktionen, mit denen einfache Klassen Konfigurationsänderungen überstehen können, ohne die vollständige Infrastruktur eines ViewModels zu benötigen. Beide Mechanismen helfen zwar bei der Statusbeibehaltung, es ist jedoch im Allgemeinen sicherer, einer beibehaltenen Instanz ein ViewModel bereitzustellen als umgekehrt, da sich ihre Lebenszyklen und Bereinigungsverhalten unterscheiden.

Die wichtigsten Vorteile der ViewModel-Klasse sind:

  • Sie ermöglicht es Ihnen, den UI-Status beizubehalten.
  • Sie bietet Zugriff auf die Geschäftslogik.

Persistenz

ViewModel ermöglicht die Persistenz sowohl durch den Status, den ein ViewModel enthält, als auch durch die Vorgänge, die ein ViewModel auslöst. Durch das Caching müssen Sie Daten bei häufigen Konfigurationsänderungen, z. B. beim Drehen des Bildschirms, nicht noch einmal abrufen.

Umfang

Wenn Sie ein ViewModel instanziieren, übergeben Sie ihm ein Objekt, das die ViewModelStoreOwner Schnittstelle implementiert. Dabei kann es sich um ein Navigationsziel, einen Navigationsgraphen, eine Aktivität oder einen anderen Typ handeln, der die Schnittstelle implementiert. Sie können ein ViewModel auch direkt auf eine zusammensetzbare Funktion beschränken, indem Sie die rememberViewModelStoreOwner API verwenden. Ihr ViewModel ist dann auf den Lebenszyklus des ViewModelStoreOwner beschränkt. Es bleibt im Arbeitsspeicher, bis der ViewModelStoreOwner endgültig entfernt wird (z. B. wenn der zusammensetzbare Inhaber die Komposition verlässt).

Eine Reihe von Klassen sind direkte oder indirekte Unterklassen der Schnittstelle ViewModelStoreOwner. Die direkten Unterklassen sind ComponentActivity und NavBackStackEntry. Eine vollständige Liste der indirekten Unterklassen finden Sie in der ViewModelStoreOwner Referenz. Wenn Sie ViewModels auf einzelne Elemente in einer LazyList oder einem Pager beschränken möchten, verwenden Sie rememberViewModelStoreProvider(), um die Inhaberverwaltung an das übergeordnete Element zu übertragen.

Wenn sich die Konfiguration der Hostaktivität ändert, werden asynchrone Vorgänge im ViewModel fortgesetzt, unabhängig davon, ob es auf die Aktivität oder auf eine bestimmte zusammensetzbare Funktion beschränkt ist. Das ist der Schlüssel zur Persistenz.

Weitere Informationen finden Sie im folgenden Abschnitt ViewModel-Lebenszyklus, unter ViewModel-Scoping-APIs, und in der Anleitung zum Status-Hoisting für Jetpack Compose.

SavedStateHandle

SavedStateHandle ermöglicht es Ihnen, Daten nicht nur bei Konfigurations änderungen, sondern auch bei Prozessabstürzen beizubehalten. So bleibt der UI-Status auch dann erhalten, wenn der Nutzer die App schließt und später wieder öffnet.

Weitere Informationen zum Speichern des UI-Status finden Sie unter UI-Status in Compose speichern.

Zugriff auf die Geschäftslogik

Obwohl die meisten Geschäftslogik in der Daten schicht vorhanden ist, kann die Benutzeroberfläche auch Geschäftslogik enthalten. Das kann der Fall sein, wenn Daten aus mehreren Repositories kombiniert werden, um den UI-Status des Bildschirms zu erstellen, oder wenn für einen bestimmten Datentyp keine Datenschicht erforderlich ist.

ViewModel ist der richtige Ort, um Geschäftslogik in der Benutzeroberfläche zu verarbeiten. Das ViewModel ist auch für die Verarbeitung von Ereignissen und deren Weiterleitung an andere Ebenen der Hierarchie zuständig, wenn Geschäftslogik angewendet werden muss, um Anwendungsdaten zu ändern.

ViewModel implementieren

Im Folgenden finden Sie eine Beispielimplementierung eines ViewModels für einen Bildschirm, auf dem der Nutzer würfeln kann.

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

Sie können dann wie folgt auf das ViewModel von einer zusammensetzbaren Funktion auf Bildschirmebene zugreifen:

import androidx.lifecycle.viewmodel.compose.viewModel

// Use the 'viewModel()' function from the lifecycle-viewmodel-compose artifact
@Composable
fun DiceRollScreen(
    viewModel: DiceRollViewModel = viewModel()
) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    // Update UI elements
}

Coroutinen mit ViewModel verwenden

ViewModel unterstützt Kotlin-Coroutinen. Es kann asynchrone Vorgänge auf dieselbe Weise beibehalten wie den UI-Status.

Weitere Informationen finden Sie unter Kotlin-Coroutinen mit Android-Architektur komponenten verwenden.

Der Lebenszyklus eines ViewModels

Der Lebenszyklus eines ViewModel ist direkt an seinen Umfang gebunden. A ViewModel bleibt im Arbeitsspeicher, bis der ViewModelStoreOwner, auf den es beschränkt ist, entfernt wird. Dies kann in den folgenden Kontexten auftreten:

  • Bei einer Aktivität, wenn sie beendet wird.
  • Bei einem Navigationseintrag, wenn er aus dem Back-Stack entfernt wird.
  • Bei einer zusammensetzbaren Funktion, wenn sie die Komposition verlässt. Mit rememberViewModelStoreOwner können Sie ein ViewModel direkt auf einen beliebigen Teil der Benutzeroberfläche beschränken (z. B. einen Pager oder eine LazyList).

ViewModels sind daher eine gute Lösung zum Speichern von Daten, die Konfigurationsänderungen überstehen.

Abbildung 1 veranschaulicht die verschiedenen Lebenszyklusstatus einer Aktivität, während sie gedreht und dann beendet wird. Die Abbildung zeigt auch die Lebensdauer des ViewModel neben dem zugehörigen Aktivitätslebenszyklus. Dieses Diagramm veranschaulicht die Status einer Aktivität.

Hier wird der Lebenszyklus eines ViewModels veranschaulicht, wenn sich der Status einer Aktivität ändert.
Abbildung 1. Lebenszyklusstatus einer Aktivität und eines ViewModels

Normalerweise fordern Sie ein ViewModel an, wenn das System zum ersten Mal die Methode onCreate() eines Aktivitätsobjekts aufruft. Das System kann onCreate() während der Lebensdauer einer Activity mehrmals aufrufen, z. B. wenn der Bildschirm eines Geräts gedreht wird. Das ViewModel ist ab dem Zeitpunkt vorhanden, an dem Sie es zum ersten Mal anfordern, bis die Aktivität beendet und zerstört wird.ViewModel

ViewModel-Abhängigkeiten löschen

Das ViewModel ruft die onCleared Methode auf, wenn der ViewModelStoreOwner es im Laufe seines Lebenszyklus zerstört. So können Sie alle Vorgänge oder Abhängigkeiten bereinigen, die dem Lebenszyklus des ViewModels folgen.

Das folgende Beispiel zeigt eine Alternative zu viewModelScope. viewModelScope ist ein integrierter CoroutineScope, der automatisch dem Lebenszyklus des ViewModels folgt. Das ViewModel verwendet es, um geschäftsbezogene Vorgänge auszulösen. Wenn Sie anstelle von viewModelScope einen benutzerdefinierten Umfang verwenden möchten, um Tests zu vereinfachen, kann das ViewModel in seinem Konstruktor einen CoroutineScope als Abhängigkeit erhalten. Wenn der ViewModelStoreOwner das ViewModel am Ende seines Lebenszyklus löscht, bricht das ViewModel auch den CoroutineScope ab.

class MyViewModel(
    private val coroutineScope: CoroutineScope =
        CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
) : ViewModel() {

    // Other ViewModel logic ...

    override fun onCleared() {
        coroutineScope.cancel()
    }
}

Ab der Lebenszyklusversion 2.5 und höher können Sie dem Konstruktor des ViewModels ein oder mehrere CloseableObjekte übergeben, die automatisch geschlossen werden, wenn die ViewModel-Instanz gelöscht wird.

class CloseableCoroutineScope(
    context: CoroutineContext = SupervisorJob() + Dispatchers.Main.immediate
) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    override fun close() {
        coroutineContext.cancel()
   }
}

class MyViewModel(
    private val coroutineScope: CoroutineScope = CloseableCoroutineScope()
) : ViewModel(coroutineScope) {
    // Other ViewModel logic ...
}

Best Practices

Im Folgenden finden Sie einige wichtige Best Practices, die Sie bei der Implementierung von ViewModel beachten sollten:

  • Verwenden Sie ViewModels aufgrund ihres Umfangs als Implementierungsdetails eines Status-Holders auf Bildschirmebene. Verwenden Sie sie nicht als Status-Holder für wiederverwendbare UI-Komponenten wie Chip-Gruppen oder Formulare. Andernfalls erhalten Sie dieselbe ViewModel-Instanz bei verschiedenen Verwendungen derselben UI-Komponente unter demselben ViewModelStoreOwner, es sei denn, Sie verwenden einen expliziten ViewModel-Schlüssel pro Chip.
  • ViewModels sollten keine Details zur UI-Implementierung kennen. Halten Sie die Namen der Methoden, die die ViewModel API bereitstellt, und die Namen der UI-Statusfelder so allgemein wie möglich. So kann Ihr ViewModel für jede Art von Benutzeroberfläche verwendet werden: Smartphone, faltbares Gerät, Tablet oder sogar Chromebook.
  • Da sie möglicherweise länger als der ViewModelStoreOwner aktiv sind, sollten ViewModels keine Verweise auf APIs im Zusammenhang mit dem Lebenszyklus wie Context oder Resources enthalten, um Speicherlecks zu vermeiden.
  • Übergeben Sie keine ViewModels an andere Klassen, Funktionen oder andere UI-Komponenten. Da sie von der Plattform verwaltet werden, sollten Sie sie so nah wie möglich an der Plattform halten – in der Nähe Ihrer Aktivität, der zusammensetzbaren Funktion auf Bildschirmebene oder des Navigationsziels. So wird verhindert, dass Komponenten auf niedrigerer Ebene auf mehr Daten und Logik zugreifen, als sie benötigen.

Weitere Informationen

Wenn Ihre Daten komplexer werden, können Sie eine separate Klasse nur zum Laden der Daten verwenden. Der Zweck von ViewModel besteht darin, die Daten für einen UI-Controller zu kapseln, damit die Daten Konfigurationsänderungen überstehen. Informationen zum Laden, Beibehalten und Verwalten von Daten bei Konfigurationsänderungen finden Sie unter Gespeicherte UI-Status.

In der Anleitung zur Android-App-Architektur wird empfohlen, eine Repository-Klasse zu erstellen, um diese Funktionen zu verarbeiten.

Zusätzliche Ressourcen

Weitere Informationen zur Klasse ViewModel finden Sie in den folgenden Ressourcen.

Dokumentation

Ansichten

Beispiele