नेविगेशन की स्थिति सेव करना और मैनेज करना

यहां दिए गए सेक्शन में, बैक स्टैक को सेव करने और बैक स्टैक में मौजूद एंट्री से जुड़ी स्थिति को सेव करने की रणनीतियों के बारे में बताया गया है.

बैक स्टैक सेव करना

यह पक्का करना ज़रूरी है कि आपके ऐप्लिकेशन की नेविगेशन स्थिति, लाइफ़साइकल के अलग-अलग इवेंट में बनी रहे. इनमें कॉन्फ़िगरेशन में होने वाले बदलाव और प्रोसेस खत्म होना शामिल है. इससे उपयोगकर्ता को बेहतर अनुभव मिलता है. 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 का अपना सबटाइप तय करता है, जिससे उसकी सभी कुंजियां इनहेरिट होती हैं, तो कस्टम 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 को पूरे Activity के बजाय, बैक स्टैक में मौजूद किसी खास NavEntry (यानी, किसी खास स्क्रीन या डेस्टिनेशन) के दायरे में रखना हो. इससे यह पक्का होता है कि ViewModel की स्थिति सिर्फ़ तब बनी रहे, जब वह खास NavEntry बैक स्टैक का हिस्सा हो. साथ ही, जब NavEntry को पॉप किया जाता है, तो स्थिति मिट जाती है.

androidx.lifecycle:lifecycle-viewmodel-navigation3 ऐड-ऑन लाइब्रेरी, NavEntryDecorator उपलब्ध कराती है, जिससे यह काम आसान हो जाता है. यह डेकोरेटर, हर NavEntry के लिए एक ViewModelStoreOwner उपलब्ध कराता है. जब Compose में viewModel() का इस्तेमाल करके, NavEntry के कॉन्टेंट में कोई ViewModel बनाया जाता है, तो वह अपने-आप बैक स्टैक में मौजूद उस खास NavEntry की कुंजी के दायरे में आ जाता है. इसका मतलब है कि ViewModel तब बनता है, जब NavEntry को बैक स्टैक में जोड़ा जाता है. साथ ही, जब इसे हटाया जाता है, तो यह मिट जाता है.

ViewModel को NavEntry के दायरे में रखने के लिए, NavEntryDecorator का इस्तेमाल करने के लिए, यह तरीका अपनाएं:

  1. अपनी app/build.gradle.kts फ़ाइल में, androidx.lifecycle:lifecycle-viewmodel-navigation3 डिपेंडेंसी जोड़ें.
  2. डिफ़ॉल्ट rememberSaveableStateHolderNavEntryDecorator() को NavDisplay बनाते समय entryDecorators की सूची में जोड़ें.
  3. entryDecorators की सूची में rememberViewModelStoreNavEntryDecorator() जोड़ें.

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 { },
)