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.
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 dieContext
enthalten. oderResources
, 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
- UI-Ebene
- Ereignisse auf der Benutzeroberfläche
- State Owners und UI-Status
- Statusproduktion
- Datenschicht
Produktproben
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Kotlin-Koroutinen mit lebenszyklusbezogenen Komponenten verwenden
- UI-Status speichern
- Daten aus Seiten laden und anzeigen