שמירה וניהול של מצב הניווט

בקטעים הבאים מתוארות אסטרטגיות לשמירת מקבץ הפעילויות הקודמות ולאחסון המצב שמשויך לרשומות במקבץ הפעילויות הקודמות.

שמירת מקבץ הפעילויות הקודמות (back stack)

כדי לספק חוויית משתמש טובה, חשוב לוודא שמצב הניווט של האפליקציה נשמר באירועים שונים במחזור החיים, כולל שינויים בהגדרות והשבתת תהליכים. ב-Navigation 3, מקבץ הפעילויות הקודמות נמצא בבעלותכם, כך שאין הנחיות מחמירות לגבי האופן שבו צריך ליצור או לשמור אותו. עם זאת, ב-Navigation 3 יש שיטה נוחה שמספקת מקבץ פעילויות קודמות שניתן לשמור: rememberNavBackStack.

שימוש ב-rememberNavBackStack

הפונקציה הקומפוזבילית rememberNavBackStack נועדה ליצור מקבץ פעילויות קודמות (back stack) שנשמר גם כשמשנים את ההגדרות וגם כשמשביתים את התהליך.

כדי ש-rememberNavBackStack יפעל כמו שצריך, כל מפתח במקבץ הפעילויות הקודמות (back stack) צריך לעמוד בדרישות ספציפיות:

  • הטמעת הממשק NavKey: כל מפתח במקבץ הפעילויות הקודמות חייב להטמיע את הממשק NavKey. הוא משמש כממשק סמן שמאותת לספרייה שאפשר לשמור את המפתח.
  • הוספת האנוטציה @Serializable: בנוסף להטמעה של NavKey, צריך לסמן את המחלקות והאובייקטים העיקריים באנוטציה @Serializable.

בקטע הקוד הבא אפשר לראות הטמעה נכונה של rememberNavBackStack:

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

זכירת מקבץ פעילויות קודמות (back stack) עם סוגי משנה של NavKey

הפונקציה הקומפוזבילית rememberNavBackStack מחזירה NavBackStack<NavKey>. אם האפליקציה מגדירה סוג משנה משלה של NavKey שכל המקשים שלה יורשים ממנו, אפשר לשמר את ההקלדה הזו על ידי הטמעה של פונקציית remember בהתאמה אישית, באופן הבא:

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

במאמר NavBackStackSamples תוכלו למצוא דוגמאות נוספות, כולל איך לטפל בפולימורפיזם פתוח.

אפשרות חלופית: אחסון ב-ViewModel

דרך נוספת לנהל את מקבץ הפעילויות הקודמות היא לאחסן אותו ב-ViewModel. כדי לשמור על עקביות במקרה של השבתת תהליך כשמשתמשים ב-ViewModel או בכל אחסון מותאם אישית אחר, צריך:

  • ודאו שאפשר להעביר את המפתחות לסדרה: בדיוק כמו ב-rememberNavBackStack, צריך לוודא שאפשר להעביר את מפתחות הניווט לסדרה.
  • טיפול ידני בסריאליזציה ובדה-סריאליזציה: אתם אחראים לשמור באופן ידני את הייצוג הסריאלי של כל מפתח באחסון מתמיד (למשל, SharedPreferences, מסד נתונים או קובץ), ולבצע דה-סריאליזציה שלו כשהאפליקציה עוברת לרקע או משוחזרת.

הגדרת ההיקף של ViewModel מופעים ל-NavEntry אובייקטים

ViewModels משמשים לשמירת המצב שקשור לממשק המשתמש בשינויים בהגדרות, כמו סיבוב המסך. כברירת מחדל, ההיקף של ViewModels הוא ViewModelStoreOwner הקרוב ביותר, שהוא בדרך כלל Activity או Fragment.

עם זאת, יכול להיות שתרצו להגדיר את ההיקף של ViewModel ל-NavEntry ספציפי (כלומר, מסך או יעד ספציפיים) במקבץ הפעילויות הקודמות (back stack), במקום לכל ה-Activity. כך אפשר לוודא שהמצב של ViewModel נשמר רק בזמן ש-NavEntry הספציפי הוא חלק ממקבץ הפעילויות הקודמות, ונמחק כש-NavEntry נשלף.

ספריית התוספים של androidx.lifecycle:lifecycle-viewmodel-navigation3 מספקת NavEntryDecorator שמאפשר את זה. המעצב הזה מספק ViewModelStoreOwner לכל NavEntry. כשיוצרים ViewModel בתוך התוכן של NavEntry (למשל, באמצעות viewModel() ב-Compose), הוא מוגדר אוטומטית למפתח של NavEntry הספציפי הזה במקבץ הפעילויות הקודמות (back stack). כלומר, ה-ViewModel נוצר כשמוסיפים את ה-NavEntry למקבץ הפעילויות הקודמות (back stack), ונמחק כשמסירים אותו.

כדי להשתמש ב-NavEntryDecorator להגדרת ההיקף של ViewModel ב-NavEntry:

  1. מוסיפים את התלות androidx.lifecycle:lifecycle-viewmodel-navigation3 לקובץ app/build.gradle.kts.
  2. מוסיפים את rememberSaveableStateHolderNavEntryDecorator() שמוגדר כברירת מחדל לרשימת ה-entryDecorators כשיוצרים NavDisplay.
  3. מוסיפים את rememberViewModelStoreNavEntryDecorator() לרשימה של entryDecorators.

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSaveableStateHolderNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)