ViewModel-Scoping-APIs   Teil von Android Jetpack.

Der Bereich ist entscheidend für die effektive Verwendung von ViewModels. Jedes ViewModel ist auf ein Objekt beschränkt, das die ViewModelStoreOwner-Schnittstelle implementiert. Es gibt mehrere APIs, mit denen Sie den Bereich Ihrer ViewModels einfacher verwalten können. In diesem Dokument werden einige der wichtigsten Techniken beschrieben, die Sie kennen sollten.

Mit der Methode ViewModelProvider.get() können Sie eine Instanz eines ViewModel abrufen, die auf ein beliebiges ViewModelStoreOwner beschränkt ist. Für Kotlin-Nutzer sind für die häufigsten Anwendungsfälle verschiedene Erweiterungsfunktionen verfügbar. Alle Implementierungen von Kotlin-Erweiterungsfunktionen verwenden die ViewModelProvider API im Hintergrund.

ViewModels, die auf den nächsten ViewModelStoreOwner beschränkt sind

Sie können ein ViewModel auf eine Composable-Funktion, eine Activity oder ein Ziel eines Navigationsdiagramms beschränken. Mit der Funktion viewModel() in Compose können Sie eine Instanz des ViewModel abrufen, die auf den nächstgelegenen ViewModelStoreOwner beschränkt ist.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the closest ViewModelStoreOwner provided
    // via the LocalViewModelStoreOwner CompositionLocal. In order of proximity,
    // this could be the destination of a Navigation graph
    // or the host Activity.
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

ViewModels mit einem beliebigen ViewModelStoreOwner als Bereich

Die Funktion viewModel() verwendet einen optionalen Parameter viewModelStoreOwner, mit dem Sie angeben können, auf welche ViewModelStoreOwner die Instanz des ViewModel beschränkt ist.

import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.ViewModelStoreOwner

@Composable
fun MyScreen(
    // A custom owner passed in, such as a parent NavBackStackEntry
    customOwner: ViewModelStoreOwner,
    // The ViewModel is now scoped to the provided customOwner
    viewModel: MyViewModel = viewModel(viewModelStoreOwner = customOwner)
) {
    /* ... */
}

View-Modelle, die auf ein Composable beschränkt sind

Mit rememberViewModelStoreOwner() können Sie ein ViewModel direkt auf den Aufrufpunkt eines Composables beschränken. Dies ist besonders nützlich für UI-Komponenten, die basierend auf dem Status dynamisch auf dem Bildschirm hinzugefügt oder entfernt werden, z. B. die Elemente einer Seite oder einer Lazy List. Wenn die zusammensetzbare Funktion, die die ViewModelStoreOwner enthält, die Komposition verlässt, wird die zugehörige ViewModelStore gelöscht und das ViewModel wird zerstört.

Verwenden Sie rememberViewModelStoreOwner(), um einen lebenszyklusbewussten Speicher zu erstellen, der Konfigurationsänderungen übersteht.

@Composable
fun RememberViewModelStoreOwnerSample() {
    // Create a ViewModelStoreOwner scoped to this specific call site.
    // When this composable leaves the composition,
    // the associated ViewModelStore will be cleared.
    val scopedOwner = rememberViewModelStoreOwner()

    CompositionLocalProvider(LocalViewModelStoreOwner provides scopedOwner) {
        // This ViewModel is scoped to `scopedOwner`.
        // It will survive configuration changes but will be cleared when
        // the composable is removed from the UI tree.
        val viewModel = viewModel { TestViewModel("scoped_data") }
        // Use the ViewModel
    }
}

Für komplexere Implementierungen, z. B. HorizontalPager oder Fälle, die mehrere unabhängige Bereiche erfordern, verwenden Sie rememberViewModelStoreProvider(). So können Sie unterschiedliche ViewModelStoreOwner-Instanzen für verschiedene Schlüssel (z. B. Seitenindexe) generieren. So behält jede Seite ihren eigenen unabhängigen ViewModel-Status bei.

@Composable
fun RememberViewModelStoreProviderSample() {
    val storeProvider = rememberViewModelStoreProvider()
    val pages = listOf("Page 1", "Page 2", "Page 3")

    HorizontalPager(pageCount = pages.size) { page ->
        // Create a ViewModelStoreOwner for the specific page using the provider.
        val pageOwner = rememberViewModelStoreOwner(provider = storeProvider, key = page)

        CompositionLocalProvider(LocalViewModelStoreOwner provides pageOwner) {
            val pageViewModel = viewModel { TestViewModel(pages[page]) }
            // Use pageViewModel
        }
    }
}

ViewModels, die auf den Navigationsgraphen beschränkt sind

Navigationsgraphen sind auch ViewModel-Speicherinhaber. Wenn Sie Navigation Compose verwenden, können Sie mit der Funktion getBackStackEntry() eine Instanz eines ViewModels abrufen, das auf einen Navigationsgraphen beschränkt ist.

viewModel() ruft die Instanz aus dem nächsten ViewModelStoreOwner ab, das vom LocalViewModelStoreOwner CompositionLocal bereitgestellt wird. In einer typischen Compose-Anwendung, in der Jetpack Navigation verwendet wird, ist dieser Eigentümer der aktuelle Navigation-Backstack-Eintrag. Das bedeutet, dass das ViewModel im Arbeitsspeicher verbleibt, solange sich das Ziel im Backstack befindet.

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        // Retrieve the NavBackStackEntry of "parentNavigationRoute"
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }
        // Get the ViewModel scoped to the `parentNavigationRoute` Nav graph
        val parentViewModel: SharedViewModel = viewModel(parentEntry)
        // ...
    }
}

Wenn Sie Hilt zusätzlich zu Jetpack Navigation verwenden, können Sie die hiltNavGraphViewModels(graphId)-API wie unten beschrieben verwenden.

import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }

        // ViewModel API available in hilt.hilt-navigation-compose
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        val parentViewModel: SharedViewModel = hiltViewModel(parentEntry)
        // ...
    }
}

Zusätzliche Ressourcen

Weitere Informationen zu ViewModels und Scope finden Sie in den folgenden zusätzlichen Ressourcen:

Dokumentation

Inhalte ansehen