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

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

שמירת ערימת החזרה

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

שימוש ב-rememberNavBackStack

הפונקציה הקומפוזבילית rememberNavBackStack נועדה ליצור מחסנית חזרה שנשמרת גם אחרי שינויים בהגדרות וגם אחרי סיום התהליך.

כדי שהלחצן rememberNavBackStack יפעל בצורה תקינה, כל מקש במצבור הפעולות הקודמות צריך לעמוד בדרישות ספציפיות:

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

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

@Serializable
data object Home : NavKey

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

תזכורת: מחסנית חזרה עם סוגי משנה של NavKey

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

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

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

כדי להשתמש ב-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 { },
)