ViewModel Scoping API'leri   Android Jetpack'in bir parçasıdır.

ViewModel'leri etkili bir şekilde kullanmak için kapsam önemlidir. Her ViewModel, ViewModelStoreOwner arayüzünü uygulayan bir nesneye göre kapsamlandırılır. ViewModel'lerinizin kapsamını daha kolay yönetmenize olanak tanıyan çeşitli API'ler vardır. Bu belgede, bilmeniz gereken bazı temel teknikler özetlenmektedir.

ViewModelProvider.get() yöntemi, herhangi bir ViewModelStoreOwner kapsamına alınmış bir ViewModel örneği almanızı sağlar. Kotlin kullanıcıları, en yaygın kullanım alanları için farklı uzantı işlevlerinden yararlanabilir. Tüm Kotlin uzantı işlevi uygulamaları, arka planda ViewModelProvider API'yi kullanır.

En yakın ViewModelStoreOwner'a göre kapsamlandırılmış ViewModel'ler

ViewModel'i bir composable, Activity veya Navigation grafiğinin hedefiyle sınırlayabilirsiniz. Compose'daki viewModel() işlevi, en yakın ViewModelStoreOwner kapsamına alınmış bir ViewModel örneği almanızı sağlar.

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()
) { /* ... */ }

ViewModelStoreOwner kapsamındaki ViewModel'ler

viewModel() işlevi, ViewModel örneğinin hangi ViewModelStoreOwner kapsamına alınacağını belirtmek için kullanabileceğiniz isteğe bağlı bir viewModelStoreOwner parametresi alır.

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)
) {
    /* ... */
}

Composable'a göre kapsamı belirlenmiş ViewModel'ler

Bir ViewModel'i doğrudan bir composable'ın çağrı sitesine kapsamlandırmak için rememberViewModelStoreOwner() kullanabilirsiniz. Bu, özellikle duruma göre ekrana dinamik olarak eklenen veya ekrandan kaldırılan kullanıcı arayüzü bileşenleri (ör. bir sayfanın öğeleri veya geç yüklenen liste) için kullanışlıdır. ViewModelStoreOwner öğesini içeren composable, kompozisyondan ayrıldığında ilişkili ViewModelStore temizlenir ve ViewModel yok edilir.

Yapılandırma değişikliklerinden etkilenmeyen, yaşam döngüsüne duyarlı bir mağaza oluşturmak için rememberViewModelStoreOwner() kullanın.

@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
    }
}

HorizontalPager gibi daha karmaşık uygulamalar veya birden fazla bağımsız kapsam gerektiren durumlar için rememberViewModelStoreProvider() kullanın. Bu sayede farklı anahtarlar (ör. sayfa dizinleri) için ayrı ViewModelStoreOwner örnekleri oluşturabilirsiniz. Bu şekilde, her sayfa kendi bağımsız ViewModel durumunu korur.

@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
        }
    }
}

Gezinme grafiği kapsamındaki ViewModels

Gezinme grafikleri de ViewModel deposu sahipleridir. Navigation Compose kullanıyorsanız getBackStackEntry() işleviyle bir gezinme grafiği kapsamına alınmış bir ViewModel örneği alabilirsiniz.

viewModel(), örneği LocalViewModelStoreOwner CompositionLocal tarafından sağlanan en yakın ViewModelStoreOwner konumundan alır. Jetpack Navigation'ın kullanıldığı tipik bir Compose uygulamasında bu sahip, mevcut Navigation geri yığını girişidir. Bu, ViewModel'in, arka yığında söz konusu hedef bulunduğu sürece bellekte kalacağı anlamına gelir.

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)
        // ...
    }
}

Jetpack Navigation'a ek olarak Hilt kullanıyorsanız hiltNavGraphViewModels(graphId) API'yi aşağıdaki gibi kullanabilirsiniz.

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)
        // ...
    }
}

Ek kaynaklar

ViewModel'ler ve kapsam hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara bakın:

Belgeler

İçeriği görüntüleme