‫ViewModel Scoping APIs   בארגז הכלים Android Jetpack.

היקף הוא המפתח לשימוש יעיל ב-ViewModels. כל ViewModel מוגדר בהיקף של אובייקט שמטמיע את הממשק ViewModelStoreOwner. יש כמה ממשקי API שמאפשרים לנהל בקלות רבה יותר את ההיקף של ViewModels. במסמך הזה מפורטות כמה מהטכניקות העיקריות שכדאי להכיר.

השיטה ViewModelProvider.get() מאפשרת לקבל מופע של ViewModel בהיקף של כל ViewModelStoreOwner. למשתמשי Kotlin, יש פונקציות הרחבה שונות שזמינות לתרחישי השימוש הנפוצים ביותר. כל ההטמעות של פונקציות ההרחבה של Kotlin משתמשות ב-ViewModelProvider API מתחת לפני השטח.

‫ViewModels בהיקף של ViewModelStoreOwner הקרוב ביותר

אפשר להגדיר את ה-ViewModel לקומפוזיציה, לפעילות או ליעד של תרשים ניווט. הפונקציה viewModel() ב-Compose מאפשרת לקבל מופע של ViewModel בהיקף של ViewModelStoreOwner הכי קרוב.

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 בהיקף של כל ViewModelStoreOwner

הפונקציה viewModel() מקבלת פרמטר אופציונלי viewModelStoreOwner שאפשר להשתמש בו כדי לציין את ViewModelStoreOwner שאליו מוגדרת ההיקף של המופע של ViewModel.

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

‫ViewModels שמוגבלים לקומפוזיציה

אפשר להשתמש ב-rememberViewModelStoreOwner() כדי להגדיר ViewModel ישירות למיקום הקריאה של פונקציה שאפשר להרכיב. האפשרות הזו שימושית במיוחד לרכיבי ממשק משתמש שנוספים למסך או מוסרים ממנו באופן דינמי על סמך מצב, כמו פריטים בדף או ברשימה עצלה. כשהקומפוזבילי שבבעלותו ViewModelStoreOwner יוצא מהקומפוזיציה, ViewModelStore המשויך מתנקה וה-ViewModel נהרס.

משתמשים ב-rememberViewModelStoreOwner() כדי ליצור מאגר נתונים שמתעדכן בהתאם למחזור החיים של האובייקטים, ושומר על הנתונים גם אחרי שינויים בהגדרות.

@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 או מקרים שבהם נדרשים כמה היקפים עצמאיים, צריך להשתמש ב-rememberViewModelStoreProvider(). כך אפשר ליצור מופעים נפרדים של ViewModelStoreOwner למפתחות שונים (כמו אינדקסים של דפים). כך, כל דף שומר על מצב ViewModel עצמאי משלו.

@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 בהיקף של תרשים הניווט

גרפים של ניווט הם גם בעלים של מאגרי ViewModel. אם אתם משתמשים ב-Navigation Compose, אתם יכולים לקבל מופע של ViewModel בהיקף של גרף ניווט באמצעות הפונקציה getBackStackEntry().

השיטה viewModel() מאחזרת את המופע מה-ViewModelStoreOwner הקרוב ביותר שסופק על ידי LocalViewModelStoreOwner CompositionLocal. באפליקציית Compose טיפוסית שמשתמשת ב-Jetpack Navigation, הבעלים הזה הוא הרשומה הנוכחית במקבץ הפעילויות הקודמות (back stack) של הניווט. כלומר, ה-ViewModel נשאר בזיכרון כל עוד היעד הזה נמצא במקבץ פעילויות קודמות (back stack).

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

אם אתם משתמשים ב-Hilt בנוסף ל-Jetpack Navigation, אתם יכולים להשתמש ב-API‏ hiltNavGraphViewModels(graphId) באופן הבא.

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

מקורות מידע נוספים

כדי לקבל מידע נוסף על ViewModels ועל היקף, אפשר לעיין במקורות המידע הנוספים הבאים:

תיעוד

צפייה בתוכן