ViewModel – Übersicht Teil von Android Jetpack

Die Klasse ViewModel ist eine Geschäftslogik oder ein Status auf Bildschirmebene. Inhaber. Er stellt den Status der Benutzeroberfläche dar und kapselt die zugehörige Geschäftslogik. Sein Hauptvorteil ist, dass er den Status im Cache speichert und Konfigurationsänderungen. Das bedeutet, dass Ihre UI Daten nicht noch einmal abrufen muss, wenn Sie zwischen Aktivitäten wechseln oder Konfigurationsänderungen wie wenn Sie den Bildschirm drehen.

Weitere Informationen zu Inhabern von Bundesstaaten finden Sie in den Richtlinien für Inhaber von Bundesstaaten. Ähnliches gilt für allgemeine Informationen zur UI-Ebene unter UI-Ebene. Anleitung.

Vorteile von ViewModel

Die Alternative zu einem ViewModel ist eine einfache Klasse, die die von Ihnen angezeigten Daten enthält. auf Ihrer Benutzeroberfläche. Dies kann zu Problemen werden, wenn Sie zwischen Aktivitäten oder Navigationsziele. Diese Daten werden zerstört, wenn Sie sie nicht speichern. mithilfe des Mechanismus zum Speichern des Instanzstatus. ViewModel bietet eine praktische API für die Datenpersistenz, die dieses Problem behebt.

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

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

Persistenz

ViewModel ermöglicht Persistenz sowohl für den Status, den ein ViewModel die Vorgänge, die ein ViewModel auslöst. Dieses Caching bedeutet, dass Sie keine zum erneuten Abrufen von Daten durch häufige Konfigurationsänderungen, wie z. B. einen Bildschirm. Rotation.

Aufgabenstellung

Wenn Sie eine ViewModel-Instanz instanziieren, übergeben Sie ein Objekt, das die Funktion ViewModelStoreOwner-Schnittstelle. Das kann ein Navigationsziel sein, Navigationsgrafiken, Aktivitäten, Fragmente oder andere Elemente zur Implementierung des . Ihr ViewModel bezieht sich dann auf den Lebenszyklus des ViewModelStoreOwner Es wird bis zu ViewModelStoreOwner gespeichert. verschwindet.

Bei einer Reihe von Klassen handelt es sich entweder um direkte oder indirekte Unterklassen der ViewModelStoreOwner-Schnittstelle. Die direkten abgeleiteten Klassen sind ComponentActivity, Fragment und NavBackStackEntry Eine vollständige Liste der indirekten abgeleiteten Klassen finden Sie in der Referenz zu ViewModelStoreOwner

Wenn das Fragment oder die Aktivität, auf die sich das ViewModel bezieht, gelöscht wird, wird die asynchrone Arbeit in der ihr zugeordneten ViewModel fortgesetzt. Dies ist die Schlüssel zur Persistenz.

Weitere Informationen finden Sie unten im Abschnitt zum ViewModel-Lebenszyklus.

SavedStateHandle

Mit SavedStateHandle können Sie Daten nicht nur durch die Konfiguration speichern sondern auch bei Neuerstellungen von Prozessen. Das heißt, Sie können die Benutzeroberfläche intakt bleibt, auch wenn der Nutzer die App schließt und zu einem späteren Zeitpunkt öffnet.

Zugriff auf Geschäftslogik

Obwohl der Großteil der Geschäftslogik in den Daten vorhanden ist kann die UI-Ebene auch Geschäftslogik enthalten. Dies kann der Fall sein, wenn Daten aus mehreren Repositories kombinieren, um den Bildschirm-UI-Status zu erstellen, oder wenn ist für einen bestimmten Datentyp keine Datenschicht erforderlich.

ViewModel ist der richtige Ort für die Verarbeitung der Geschäftslogik auf der UI-Ebene. Die ViewModel ist auch für die Verarbeitung von Ereignissen zuständig und delegiert sie an andere Hierarchieebenen ein, wenn Geschäftslogik angewendet werden muss, um Anwendungsdaten.

Jetpack Compose

Bei Verwendung von Jetpack Compose ist ViewModel die Hauptmethode zur Anzeige der Bildschirm-UI zu den zusammensetzbaren Funktionen hinzu. In einer hybriden Anwendung werden Aktivitäten und Fragmente die zusammensetzbaren Funktionen verwenden. Dies ist ein Wechsel von früheren Ansätzen, bei denen es können Sie einfach und intuitiv wiederverwendbare UI-Elemente mit Aktivitäten sodass sie als UI-Controller viel aktiver wurden.

Der wichtigste Punkt bei der Verwendung von ViewModel mit Compose ist dass Sie ein ViewModel nicht auf eine zusammensetzbare Funktion anwenden können. Das liegt daran, dass eine zusammensetzbare Funktion ist kein ViewModelStoreOwner. Zwei Instanzen derselben zusammensetzbaren Funktion in der Komposition oder zwei verschiedene zusammensetzbare Funktionen, die auf denselben ViewModel-Typ zugreifen unter derselben ViewModelStoreOwner erhalten dieselbe Instanz von ViewModel erstellt, was häufig nicht dem erwarteten Verhalten entspricht.

Hosten Sie jeden Bildschirm in einem Fragment, um die Vorteile von ViewModel in Compose zu nutzen oder Activity oder Compose Navigation und ViewModels in zusammensetzbaren möglichst nah am Navigationsziel liegt. Das liegt daran, können Sie ein ViewModel auf Navigationsziele, Navigationsdiagramme, Aktivitäten und Fragmente.

Weitere Informationen finden Sie im Leitfaden zu Zustandswinden für Jetpack Compose.

ViewModel implementieren

Im Folgenden finden Sie eine Beispielimplementierung eines ViewModel für einen Bildschirm, der ermöglicht es dem Nutzer, Würfel zu werfen.

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 über eine Aktivität wie folgt auf die ViewModel-Ressource 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
        });
    }
}

Jetpack Compose

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
}

Koroutinen mit ViewModel verwenden

ViewModel unterstützt Kotlin-Koroutinen. Sie kann weiterhin wie der UI-Status beibehalten wird.

Weitere Informationen finden Sie unter Kotlin-Koroutinen mit der Android-Architektur verwenden. Komponenten.

Der Lebenszyklus einer ViewModel-Ressource

Der Lebenszyklus einer ViewModel ist direkt mit ihrem Umfang verbunden. Ein ViewModel bleibt im Arbeitsspeicher, bis ViewModelStoreOwner, dem sie zugeordnet ist, verschwindet. Dies kann in den folgenden Kontexten vorkommen:

  • Im Fall einer Aktivität, wenn diese beendet ist.
  • Ein Fragment, das sich löst.
  • Im Falle eines Navigationseintrags, wenn er aus dem Back Stack entfernt wird.

Daher ist ViewModels eine hervorragende Lösung zum Speichern von Daten, die Konfigurationsänderungen.

In Abbildung 1 sind die verschiedenen Lebenszykluszustände einer Aktivität dargestellt. eine Rotation und ist dann fertig. Die Abbildung zeigt auch die Lebensdauer des ViewModel neben dem zugehörigen Aktivitätslebenszyklus. Diese spezielle Das Diagramm veranschaulicht den Status einer Aktivität. Dieselben Grundzustände gelten für des Lebenszyklus eines Fragments.

Veranschaulicht den Lebenszyklus eines ViewModel in Bezug auf den Status einer Aktivitätsänderung.

Normalerweise fordern Sie ein ViewModel an, wenn das System zum ersten Mal eine onCreate()-Methode des Aktivitätsobjekts. Das System ruft möglicherweise onCreate() während einer Aktivität mehrmals, z. B. als wenn der Bildschirm des Geräts gedreht wird. Die ViewModel existiert aus der Zeit, in der Sie fordern Sie erst eine ViewModel an, bis die Aktivität abgeschlossen und gelöscht ist.

ViewModel-Abhängigkeiten löschen

ViewModel ruft die Methode onCleared auf, wenn die ViewModelStoreOwner und zerstört es während seines Lebenszyklus. So können Sie alle Aufgaben, oder Abhängigkeiten, die dem Lebenszyklus von ViewModel folgen.

Das folgende Beispiel zeigt eine Alternative zu viewModelScope. viewModelScope ist ein integriertes CoroutineScope, das automatisch dem Lebenszyklus der ViewModel. ViewModel nutzt es, um geschäftsbezogene Abläufe auslösen. Wenn Sie stattdessen einen benutzerdefinierten Umfang verwenden möchten von viewModelScope für einfachere Tests erhalten, kann ViewModel eine CoroutineScope als Abhängigkeit im Konstruktor. Wenn der Parameter ViewModelStoreOwner löscht ViewModel am Ende seines Lebenszyklus. ViewModel bricht außerdem CoroutineScope ab.

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

    // Other ViewModel logic ...

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

Ab Lebenszyklus Version 2.5 und höher können Sie einen oder mehrere Closeable übergeben. -Objekt an den ViewModel-Konstruktor, der automatisch geschlossen wird, wenn die ViewModel-Instanz wurde gelöscht.

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:

  • Verwenden Sie aufgrund des Gültigkeitsbereichs ViewModels als Implementierungsdetails eines Statusinhaber auf Bildschirmebene. Verwenden Sie sie nicht als Statusinhaber einer wiederverwendbaren UI. wie Chipgruppen oder Formen. Andernfalls erhalten Sie dasselbe ViewModel-Instanz in unterschiedlichen Verwendungen derselben UI-Komponente unter derselben ViewModelStoreOwner, sofern Sie keinen expliziten Ansichtsmodellschlüssel pro Chip verwenden.
  • ViewModels sollte die Details der UI-Implementierung nicht kennen. Namen beibehalten Methoden, die die ViewModel API bereitstellt, und die der UI-Statusfelder als wie möglich allgemein sein. So kann Ihr ViewModel jede Art von Benutzeroberfläche: Smartphone, faltbares Tablet, Tablet oder sogar Chromebook
  • Da sie möglicherweise länger leben als die ViewModelStoreOwner, sehen Sie sich Modelle an. sollte keine Verweise auf lebenszyklusbezogene APIs wie die Context enthalten. oder Resources, um Speicherlecks zu vermeiden.
  • Übergeben Sie ViewModels nicht an andere Klassen, Funktionen oder andere UI-Komponenten. Da sie von der Plattform verwaltet werden, sollten Sie sie so nah wie möglich in der Nähe halten, können. Nah an der zusammensetzbaren Funktion auf Aktivitäts-, Fragment- oder Bildschirmebene. Dies verhindert, dass untergeordnete Komponenten auf mehr Daten und Logik zugreifen die sie brauchen.

Weitere Informationen

Wenn Ihre Daten immer komplexer werden, können Sie sich für eine separate Klasse entscheiden, um die Daten zu laden. Der Zweck von ViewModel besteht darin, die Daten für einen UI-Controller, damit die Daten Konfigurationsänderungen überleben. Weitere Informationen zum Laden, Beibehalten und Verwalten von Daten über Konfigurationsänderungen hinweg Gespeicherte UI-Status:

Im Leitfaden zur Android-App-Architektur wird empfohlen, eine Repository-Klasse zu erstellen. um diese Funktionen zu verarbeiten.

Weitere Informationen

Weitere Informationen zur Klasse ViewModel finden Sie hier: Ressourcen.

Dokumentation

Produktproben