राज्य के मालिक और यूज़र इंटरफ़ेस (यूआई) की स्थिति

यूज़र इंटरफ़ेस (यूआई) लेयर की गाइड में, यूज़र इंटरफ़ेस (यूआई) लेयर के लिए यूज़र इंटरफ़ेस (यूआई) स्टेट को जनरेट और मैनेज करने के तरीके के तौर पर, एकतरफ़ा डेटा फ़्लो (यूडीएफ़) के बारे में बताया गया है.

डेटा लेयर से यूज़र इंटरफ़ेस (यूआई) तक डेटा एक ही दिशा में फ़्लो होता है.
पहली इमेज. एकतरफ़ा डेटा फ़्लो.

इसमें, यूडीएफ़ को मैनेज करने की ज़िम्मेदारी, स्टेट होल्डर नाम की खास क्लास को सौंपने के फ़ायदों के बारे में भी बताया गया है. स्टेट होल्डर को ViewModel या सामान्य क्लास के ज़रिए लागू किया जा सकता है. इस दस्तावेज़ में, स्टेकहोल्डर और यूज़र इंटरफ़ेस (यूआई) लेयर में उनकी भूमिका के बारे में ज़्यादा जानकारी दी गई है.

इस दस्तावेज़ को पढ़ने के बाद, आपको यूज़र इंटरफ़ेस (यूआई) लेयर में ऐप्लिकेशन की स्थिति को मैनेज करने का तरीका पता होना चाहिए. इसे यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन पाइपलाइन कहा जाता है. आपको इनके बारे में जानकारी होनी चाहिए:

  • यूज़र इंटरफ़ेस (यूआई) लेयर में मौजूद यूज़र इंटरफ़ेस (यूआई) की अलग-अलग स्थितियों के बारे में जानें.
  • यूज़र इंटरफ़ेस (यूआई) लेयर में, यूज़र इंटरफ़ेस (यूआई) की उन स्थितियों पर काम करने वाले लॉजिक के टाइप को समझें.
  • स्टेट होल्डर, जैसे कि ViewModel या क्लास को लागू करने का सही तरीका जानें.

यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन पाइपलाइन के एलिमेंट

यूज़र इंटरफ़ेस (यूआई) की स्थिति और उसे जनरेट करने वाला लॉजिक, यूज़र इंटरफ़ेस (यूआई) लेयर को तय करता है.

यूज़र इंटरफ़ेस (यूआई) की स्थिति

यूज़र इंटरफ़ेस (यूआई) की स्थिति वह प्रॉपर्टी है जो यूज़र इंटरफ़ेस (यूआई) के बारे में बताती है. यूज़र इंटरफ़ेस (यूआई) की स्थिति दो तरह की होती है:

  • स्क्रीन यूज़र इंटरफ़ेस (यूआई) की स्थिति वह जानकारी होती है जिसे आपको स्क्रीन पर दिखाना होता है. उदाहरण के लिए, NewsUiState क्लास में, खबरों से जुड़े लेख और यूज़र इंटरफ़ेस (यूआई) को रेंडर करने के लिए ज़रूरी अन्य जानकारी शामिल हो सकती है. यह आम तौर पर, क्रम की अन्य लेयर से जुड़ा होता है, क्योंकि इसमें ऐप्लिकेशन का डेटा होता है.
  • यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति से मतलब, यूज़र इंटरफ़ेस (यूआई) एलिमेंट की उन प्रॉपर्टी से है जो यह तय करती हैं कि उन्हें कैसे रेंडर किया जाएगा. यूआई एलिमेंट को दिखाया या छिपाया जा सकता है. साथ ही, इसमें कोई फ़ॉन्ट, फ़ॉन्ट साइज़ या फ़ॉन्ट का रंग हो सकता है. Jetpack Compose में, स्टेट कंपोज़ेबल के बाहर होती है. इसे कंपोज़ेबल के आस-पास से हटाकर, कंपोज़ेबल फ़ंक्शन या स्टेट होल्डर को कॉल करने वाले कंपोज़ेबल में भी ले जाया जा सकता है. इसका एक उदाहरण, Scaffold कंपोज़ेबल के लिए ScaffoldState है.

लॉजिक

यूआई की स्थिति एक जैसी नहीं रहती, क्योंकि ऐप्लिकेशन के डेटा और उपयोगकर्ता के इवेंट की वजह से, यूआई की स्थिति समय के साथ बदलती रहती है. लॉजिक से यह तय होता है कि बदलाव किस तरह का होगा. जैसे, यूज़र इंटरफ़ेस (यूआई) की स्थिति के किन हिस्सों में बदलाव हुआ है, बदलाव क्यों हुआ है, और बदलाव कब होना चाहिए.

लॉजिक से यूज़र इंटरफ़ेस (यूआई) की स्थिति तय होती है
दूसरी इमेज. यूज़र इंटरफ़ेस (यूआई) की स्थिति को जनरेट करने वाला लॉजिक.

किसी ऐप्लिकेशन में लॉजिक, कारोबारी नियम या यूज़र इंटरफ़ेस (यूआई) लॉजिक हो सकता है:

  • कारोबारी नियम, ऐप्लिकेशन के डेटा के लिए प्रॉडक्ट की ज़रूरी शर्तों को लागू करने की प्रोसेस है. उदाहरण के लिए, जब कोई उपयोगकर्ता बटन पर टैप करता है, तब किसी समाचार पढ़ने वाले ऐप्लिकेशन में किसी लेख को बुकमार्क करना. किसी फ़ाइल या डेटाबेस में बुकमार्क सेव करने का यह लॉजिक, आम तौर पर डोमेन या डेटा लेयर में रखा जाता है. आम तौर पर, स्टेट होल्डर इस लॉजिक को उन लेयर को सौंपता है जो तरीके उपलब्ध कराती हैं.
  • यूज़र इंटरफ़ेस (यूआई) लॉजिक का संबंध, स्क्रीन पर यूज़र इंटरफ़ेस (यूआई) की स्थिति को कैसे दिखाया जाए, इससे होता है. उदाहरण के लिए, जब उपयोगकर्ता कोई कैटगरी चुनता है, तो उसे सही खोज बार का सुझाव मिलता है. इसके अलावा, सूची में किसी खास आइटम पर स्क्रोल करना या जब उपयोगकर्ता किसी बटन पर क्लिक करता है, तो उसे किसी खास स्क्रीन पर ले जाने का नेविगेशन लॉजिक.

Android लाइफ़साइकल और यूज़र इंटरफ़ेस (यूआई) की स्थिति और लॉजिक के टाइप

यूज़र इंटरफ़ेस लेयर के दो हिस्से होते हैं: एक यूज़र इंटरफ़ेस के लाइफ़साइकल पर निर्भर करता है और दूसरा इससे अलग होता है. इस तरह से अलग-अलग करने पर, हर हिस्से के लिए उपलब्ध डेटा सोर्स तय होते हैं. इसलिए, इसके लिए अलग-अलग तरह की यूज़र इंटरफ़ेस (यूआई) स्थिति और लॉजिक की ज़रूरत होती है.

  • यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल से अलग: यूज़र इंटरफ़ेस (यूआई) लेयर का यह हिस्सा, ऐप्लिकेशन की डेटा जनरेट करने वाली लेयर (डेटा या डोमेन लेयर) से जुड़ा होता है. इसे कारोबार के लॉजिक के हिसाब से तय किया जाता है. लाइफ़साइकल, कॉन्फ़िगरेशन में बदलाव, और यूज़र इंटरफ़ेस (यूआई) में Activity फिर से बनाना, यूज़र इंटरफ़ेस (यूआई) की स्थिति को बनाए रखने वाली पाइपलाइन के चालू होने पर असर डाल सकता है. हालांकि, इससे जनरेट किए गए डेटा की वैधता पर कोई असर नहीं पड़ता.
  • यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल पर निर्भर: यूज़र इंटरफ़ेस (यूआई) लेयर का यह हिस्सा, यूज़र इंटरफ़ेस (यूआई) के लॉजिक से जुड़ा होता है. साथ ही, यह लाइफ़साइकल या कॉन्फ़िगरेशन में हुए बदलावों से सीधे तौर पर प्रभावित होता है. इन बदलावों से, इसमें पढ़े गए डेटा के सोर्स की पुष्टि पर सीधे तौर पर असर पड़ता है. इसलिए, इसकी स्थिति सिर्फ़ तब बदल सकती है, जब इसका लाइफ़साइकल चालू हो. इसके उदाहरणों में, रनटाइम अनुमतियां और कॉन्फ़िगरेशन पर निर्भर संसाधन पाना शामिल है. जैसे, स्थानीय भाषा में अनुवादित स्ट्रिंग.

ऊपर दी गई जानकारी को इस टेबल में देखा जा सकता है:

यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल से अलग यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल पर निर्भर
कारोबार से जुड़ा लॉजिक यूज़र इंटरफ़ेस (यूआई) का लॉजिक
स्क्रीन के यूज़र इंटरफ़ेस (यूआई) की स्थिति

यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन पाइपलाइन

यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन पाइपलाइन से मतलब, यूज़र इंटरफ़ेस (यूआई) स्टेट बनाने के लिए किए गए चरणों से है. इन चरणों में, पहले तय किए गए लॉजिक के टाइप लागू किए जाते हैं. ये पूरी तरह से आपके यूज़र इंटरफ़ेस (यूआई) की ज़रूरतों पर निर्भर करते हैं. कुछ यूज़र इंटरफ़ेस (यूआई) को पाइपलाइन के यूज़र इंटरफ़ेस (यूआई) लाइफ़साइकल से जुड़े और यूज़र इंटरफ़ेस (यूआई) लाइफ़साइकल से अलग, दोनों हिस्सों से फ़ायदा मिल सकता है. ऐसा हो सकता है कि उन्हें किसी एक हिस्से से फ़ायदा मिले या किसी से भी न मिले.

इसका मतलब है कि यूज़र इंटरफ़ेस (यूआई) लेयर पाइपलाइन के ये क्रम मान्य हैं:

  • यूज़र इंटरफ़ेस (यूआई) की स्थिति, यूज़र इंटरफ़ेस (यूआई) से ही जनरेट और मैनेज की जाती है. उदाहरण के लिए, एक सामान्य, बार-बार इस्तेमाल किया जा सकने वाला बेसिक काउंटर:

    @Composable
    fun Counter() {
        // The UI state is managed by the UI itself
        var count by remember { mutableStateOf(0) }
        Row {
            Button(onClick = { ++count }) {
                Text(text = "Increment")
            }
            Button(onClick = { --count }) {
                Text(text = "Decrement")
            }
        }
    }
    
  • यूज़र इंटरफ़ेस (यूआई) का लॉजिक → यूज़र इंटरफ़ेस (यूआई). उदाहरण के लिए, किसी ऐसे बटन को दिखाना या छिपाना जिससे उपयोगकर्ता सूची में सबसे ऊपर जा सके.

    @Composable
    fun ContactsList(contacts: List<Contact>) {
        val listState = rememberLazyListState()
        val isAtTopOfList by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex < 3
            }
        }
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Show or hide the button (UI logic) based on the list scroll position
        AnimatedVisibility(visible = !isAtTopOfList) {
            ScrollToTopButton()
        }
    }
    
  • कारोबारी नियम → यूज़र इंटरफ़ेस (यूआई). यूज़र इंटरफ़ेस (यूआई) एलिमेंट, जो स्क्रीन पर मौजूदा उपयोगकर्ता की फ़ोटो दिखाता है.

    @Composable
    fun UserProfileScreen(viewModel: UserProfileViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
        // Call on the UserAvatar Composable to display the photo
        UserAvatar(picture = uiState.profilePicture)
    }
    
  • कारोबारी नियम → यूज़र इंटरफ़ेस (यूआई) का लॉजिक → यूज़र इंटरफ़ेस (यूआई). यह एक यूज़र इंटरफ़ेस (यूआई) एलिमेंट है. यह स्क्रोल करके, यूज़र इंटरफ़ेस (यूआई) की किसी स्थिति के लिए स्क्रीन पर सही जानकारी दिखाता है.

    @Composable
    fun ContactsList(viewModel: ContactsViewModel = hiltViewModel()) {
        // Read screen UI state from the business logic state holder
        val uiState by viewModel.uiState.collectAsStateWithLifecycle()
        val contacts = uiState.contacts
        val deepLinkedContact = uiState.deepLinkedContact
    
        val listState = rememberLazyListState()
    
        // Create the LazyColumn with the lazyListState
        ...
    
        // Perform UI logic that depends on information from business logic
        if (deepLinkedContact != null && contacts.isNotEmpty()) {
            LaunchedEffect(listState, deepLinkedContact, contacts) {
                val deepLinkedContactIndex = contacts.indexOf(deepLinkedContact)
                if (deepLinkedContactIndex >= 0) {
                  // Scroll to deep linked item
                  listState.animateScrollToItem(deepLinkedContactIndex)
                }
            }
        }
    }
    

अगर यूज़र इंटरफ़ेस (यूआई) की स्थिति को तैयार करने वाली पाइपलाइन में, दोनों तरह के लॉजिक लागू किए जाते हैं, तो बिज़नेस लॉजिक को हमेशा यूज़र इंटरफ़ेस (यूआई) लॉजिक से पहले लागू किया जाना चाहिए. यूज़र इंटरफ़ेस (यूआई) लॉजिक के बाद कारोबारी नियम लागू करने का मतलब है कि कारोबारी नियम, यूज़र इंटरफ़ेस (यूआई) लॉजिक पर निर्भर करता है. यहां दिए गए सेक्शन में बताया गया है कि यह समस्या क्यों है. इसके लिए, अलग-अलग लॉजिक टाइप और उनके स्टेट होल्डर के बारे में विस्तार से बताया गया है.

डेटा, डेटा तैयार करने वाली लेयर से यूज़र इंटरफ़ेस (यूआई) तक जाता है
तीसरी इमेज. यूज़र इंटरफ़ेस लेयर में लॉजिक लागू करना.

स्टेट होल्डर और उनकी ज़िम्मेदारियां

स्टेट होल्डर की ज़िम्मेदारी, स्टेट को सेव करना है, ताकि ऐप्लिकेशन उसे पढ़ सके. जिन मामलों में लॉजिक की ज़रूरत होती है उनमें यह इंटरमीडियरी के तौर पर काम करता है. साथ ही, उन डेटा सोर्स का ऐक्सेस देता है जिनमें ज़रूरी लॉजिक मौजूद होता है. इस तरह, स्टेट होल्डर, लॉजिक को सही डेटा सोर्स को सौंप देता है.

इससे ये फ़ायदे मिलते हैं:

  • आसान यूज़र इंटरफ़ेस (यूआई): यूज़र इंटरफ़ेस (यूआई) सिर्फ़ अपनी स्थिति को बाइंड करता है.
  • बदलाव करना आसान: स्टेट होल्डर में तय किए गए लॉजिक को यूज़र इंटरफ़ेस (यूआई) में बदलाव किए बिना दोहराया जा सकता है.
  • जांच करने की सुविधा: यूज़र इंटरफ़ेस (यूआई) और इसके स्टेट प्रोडक्शन लॉजिक की अलग से जांच की जा सकती है.
  • पढ़ने में आसानी: कोड पढ़ने वाले लोग, यूज़र इंटरफ़ेस (यूआई) प्रज़ेंटेशन कोड और यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन कोड के बीच के अंतर को साफ़ तौर पर देख सकते हैं.

यूज़र इंटरफ़ेस (यूआई) के हर एलिमेंट का, उसके स्टेट होल्डर के साथ 1:1 का संबंध होता है. भले ही, उसका साइज़ या स्कोप कुछ भी हो. इसके अलावा, स्टेट होल्डर को उपयोगकर्ता की ऐसी किसी भी कार्रवाई को स्वीकार और प्रोसेस करना चाहिए जिससे यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव हो सकता है. साथ ही, उसे स्थिति में होने वाले बदलाव को दिखाना चाहिए.

स्टेट होल्डर के टाइप

यूज़र इंटरफ़ेस (यूआई) की स्थिति और लॉजिक की तरह ही, यूज़र इंटरफ़ेस (यूआई) लेयर में दो तरह के स्टेट होल्डर होते हैं. इन्हें यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल के हिसाब से तय किया जाता है:

  • कारोबारी नियम की स्थिति को मैनेज करने वाला.
  • यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर.

यहां दिए गए सेक्शन में, अलग-अलग तरह के स्टेट होल्डर के बारे में ज़्यादा जानकारी दी गई है. इसमें सबसे पहले, कारोबार के लॉजिक वाले स्टेट होल्डर के बारे में बताया गया है.

कारोबारी लॉजिक और उसके स्टेट होल्डर

बिज़नेस लॉजिक स्टेट होल्डर, उपयोगकर्ता के इवेंट प्रोसेस करते हैं. साथ ही, डेटा या डोमेन लेयर से स्क्रीन यूज़र इंटरफ़ेस (यूआई) की स्थिति में डेटा को बदलते हैं. Android लाइफ़साइकल और ऐप्लिकेशन के कॉन्फ़िगरेशन में होने वाले बदलावों को ध्यान में रखते हुए, लोगों को बेहतर अनुभव देने के लिए, कारोबार के लॉजिक का इस्तेमाल करने वाले स्टेट होल्डर में ये प्रॉपर्टी होनी चाहिए:

प्रॉपर्टी विवरण
यूज़र इंटरफ़ेस (यूआई) स्टेट जनरेट करता है बिज़नेस लॉजिक स्टेट होल्डर, अपने यूज़र इंटरफ़ेस (यूआई) के लिए यूआई स्टेट जनरेट करने के लिए ज़िम्मेदार होते हैं. यूज़र इंटरफ़ेस (यूआई) की यह स्थिति, अक्सर उपयोगकर्ता के इवेंट को प्रोसेस करने और डोमेन और डेटा लेयर से डेटा पढ़ने की वजह से होती है.
गतिविधि को फिर से शुरू करने पर बनाए रखा जाता है कारोबार के लॉजिक की स्थिति को बनाए रखने वाले कॉम्पोनेंट, Activity फिर से बनाने पर भी अपनी स्थिति और स्थिति को प्रोसेस करने वाली पाइपलाइन को बनाए रखते हैं. इससे उपयोगकर्ताओं को बेहतर अनुभव मिलता है. अगर स्टेट होल्डर को बनाए नहीं रखा जा सकता और उसे फिर से बनाया जाता है (आम तौर पर प्रोसेस बंद होने के बाद), तो स्टेट होल्डर को अपनी पिछली स्थिति को आसानी से फिर से बनाने में सक्षम होना चाहिए. इससे यह पक्का किया जा सकेगा कि उपयोगकर्ता को एक जैसा अनुभव मिले.
लंबे समय तक स्थिति बनाए रखना कारोबार के लॉजिक की स्थिति को बनाए रखने वाले ऑब्जेक्ट का इस्तेमाल अक्सर नेविगेशन डेस्टिनेशन के लिए स्थिति को मैनेज करने के लिए किया जाता है. इस वजह से, नेविगेशन में बदलाव होने पर भी, वे अक्सर अपनी स्थिति बनाए रखते हैं. ऐसा तब तक होता है, जब तक उन्हें नेविगेशन ग्राफ़ से हटा नहीं दिया जाता.
यह अपने यूज़र इंटरफ़ेस (यूआई) के लिए यूनीक होता है और इसे फिर से इस्तेमाल नहीं किया जा सकता आम तौर पर, कारोबार के लॉजिक की स्थिति को बनाए रखने वाले कॉम्पोनेंट, किसी ऐप्लिकेशन के फ़ंक्शन के लिए स्थिति बनाते हैं. उदाहरण के लिए, TaskEditViewModel या TaskListViewModel. इसलिए, ये कॉम्पोनेंट सिर्फ़ उस ऐप्लिकेशन के फ़ंक्शन पर लागू होते हैं. एक ही स्टेट होल्डर, अलग-अलग साइज़, डाइमेंशन या कॉन्फ़िगरेशन वाले डिवाइसों पर, ऐप्लिकेशन के इन फ़ंक्शन को सपोर्ट कर सकता है. उदाहरण के लिए, ऐप्लिकेशन के मोबाइल, टीवी, और टैबलेट वर्शन, कारोबार के लॉजिक के लिए एक ही स्टेट होल्डर का फिर से इस्तेमाल कर सकते हैं.

उदाहरण के लिए, "Now in Android" ऐप्लिकेशन में लेखक के नेविगेशन डेस्टिनेशन पर जाएं:

Now in Android ऐप्लिकेशन में यह दिखाया गया है कि ऐप्लिकेशन के मुख्य फ़ंक्शन को दिखाने वाले नेविगेशन डेस्टिनेशन के पास, कारोबार के लॉजिक की स्थिति को सेव करने वाला अपना यूनीक ऑब्जेक्ट होना चाहिए.
चौथी इमेज. Now in Android ऐप्लिकेशन.

कारोबार के लॉजिक के स्टेट होल्डर के तौर पर काम करने वाला AuthorViewModel, इस मामले में यूज़र इंटरफ़ेस (यूआई) की स्थिति को इस तरह से दिखाता है:

@HiltViewModel
class AuthorViewModel @Inject constructor(
    savedStateHandle: SavedStateHandle,
    private val authorsRepository: AuthorsRepository,
    newsRepository: NewsRepository
) : ViewModel() {

    val uiState: StateFlow<AuthorScreenUiState> = 

    // Business logic
    fun followAuthor(followed: Boolean) {
      
    }
}

ध्यान दें कि AuthorViewModel में वे एट्रिब्यूट हैं जिनके बारे में पहले बताया गया था:

प्रॉपर्टी विवरण
AuthorScreenUiState की बनाई फ़िल्में या शो AuthorViewModel, AuthorsRepository और NewsRepository से डेटा पढ़ता है. इसके बाद, उस डेटा का इस्तेमाल करके AuthorScreenUiState जनरेट करता है. जब कोई उपयोगकर्ता AuthorsRepository को डेलिगेट करके, किसी Author को फ़ॉलो या अनफ़ॉलो करना चाहता है, तब यह कारोबार के लॉजिक को भी लागू करता है.
डेटा लेयर का ऐक्सेस हो इसके कंस्ट्रक्टर में AuthorsRepository और NewsRepository का एक इंस्टेंस पास किया जाता है. इससे, Author को फ़ॉलो करने के कारोबार के लॉजिक को लागू किया जा सकता है.
Activity मनोरंजन के लिए उपलब्ध है इसे ViewModel की मदद से लागू किया जाता है. इसलिए, Activity को तेज़ी से फिर से बनाने पर भी इसे बनाए रखा जाएगा. प्रोसेस बंद होने पर, SavedStateHandle ऑब्जेक्ट को पढ़ा जा सकता है. इससे, डेटा लेयर से यूज़र इंटरफ़ेस (यूआई) की स्थिति को वापस लाने के लिए ज़रूरी कम से कम जानकारी मिलती है.
इस कुकी में लंबे समय तक बनी रहने वाली स्थिति होती है ViewModel को नेविगेशन ग्राफ़ के हिसाब से स्कोप किया जाता है. इसलिए, जब तक ऑथर डेस्टिनेशन को नेविगेशन ग्राफ़ से नहीं हटाया जाता, तब तक uiState StateFlow में यूज़र इंटरफ़ेस (यूआई) की स्थिति मेमोरी में बनी रहती है. StateFlow का इस्तेमाल करने से, कारोबार के लॉजिक को लागू करने का फ़ायदा भी मिलता है. इससे स्टेट को लेज़ी तरीके से जनरेट किया जाता है, क्योंकि स्टेट सिर्फ़ तब जनरेट होती है, जब यूज़र इंटरफ़ेस (यूआई) स्टेट का कोई कलेक्टर होता है.
यह अपने यूज़र इंटरफ़ेस (यूआई) के लिए यूनीक है AuthorViewModel सिर्फ़ लेखक के नेविगेशन डेस्टिनेशन पर लागू होता है. इसका इस्तेमाल किसी दूसरी जगह पर नहीं किया जा सकता. अगर कोई ऐसा बिज़नेस लॉजिक है जिसका इस्तेमाल नेविगेशन के सभी डेस्टिनेशन पर किया जाता है, तो उस बिज़नेस लॉजिक को डेटा या डोमेन-लेयर-स्कोप वाले कॉम्पोनेंट में शामिल किया जाना चाहिए.

कारोबारी नियमों की स्थिति को बनाए रखने वाले ViewModel

Android डेवलपमेंट में ViewModels के फ़ायदों की वजह से, ये इन कामों के लिए सही होते हैं: कारोबारी नियमों का ऐक्सेस देना और स्क्रीन पर दिखाने के लिए ऐप्लिकेशन डेटा तैयार करना. इन फ़ायदों में ये शामिल हैं:

  • ViewModels से ट्रिगर किए गए ऑपरेशन, कॉन्फ़िगरेशन में बदलाव के दौरान भी बने रहते हैं.
  • नेविगेशन के साथ इंटिग्रेशन:
    • स्क्रीन के बैक स्टैक में होने पर, नेविगेशन ViewModels को कैश मेमोरी में सेव करता है. इससे, डेस्टिनेशन पर वापस आने पर, पहले से लोड किया गया डेटा तुरंत उपलब्ध हो जाता है. कंपोज़ेबल स्क्रीन के लाइफ़साइकल को फ़ॉलो करने वाले स्टेट होल्डर के साथ ऐसा करना ज़्यादा मुश्किल होता है.
    • जब डेस्टिनेशन को बैक स्टैक से हटा दिया जाता है, तब ViewModel भी मिट जाता है. इससे यह पक्का होता है कि आपकी स्थिति अपने-आप साफ़ हो गई है. यह कंपोज़ेबल के डिस्पोज़ल के लिए सुनने से अलग है. ऐसा कई वजहों से हो सकता है. जैसे, नई स्क्रीन पर जाना, कॉन्फ़िगरेशन में बदलाव होना या अन्य वजहें.
  • Hilt जैसी अन्य Jetpack लाइब्रेरी के साथ इंटिग्रेशन.

यूज़र इंटरफ़ेस (यूआई) लॉजिक और उसका स्टेट होल्डर

यूज़र इंटरफ़ेस (यूआई) लॉजिक, यूज़र इंटरफ़ेस (यूआई) से मिले डेटा पर काम करता है. यह यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति या यूज़र इंटरफ़ेस (यूआई) डेटा सोर्स पर हो सकता है. जैसे, अनुमतियों का एपीआई या Resources. यूज़र इंटरफ़ेस (यूआई) लॉजिक का इस्तेमाल करने वाले स्टेट होल्डर में आम तौर पर ये प्रॉपर्टी होती हैं:

  • यह कुकी, यूज़र इंटरफ़ेस (यूआई) की स्थिति जनरेट करती है और यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को मैनेज करती है.
  • फिर से बनाने पर भी मौजूद नहीं रहता Activity: यूज़र इंटरफ़ेस (यूआई) लॉजिक में होस्ट किए गए स्टेट होल्डर, अक्सर यूज़र इंटरफ़ेस (यूआई) के डेटा सोर्स पर निर्भर होते हैं. साथ ही, कॉन्फ़िगरेशन में होने वाले बदलावों के दौरान इस जानकारी को बनाए रखने की कोशिश करने से, अक्सर मेमोरी लीक होती है. अगर स्टेट होल्डर को कॉन्फ़िगरेशन में बदलाव होने पर भी डेटा को बनाए रखने की ज़रूरत है, तो उन्हें किसी ऐसे कॉम्पोनेंट को डेटा सौंपना होगा जो Activity को फिर से बनाने के लिए ज़्यादा सही हो. उदाहरण के लिए, Jetpack Compose में remembered फ़ंक्शन की मदद से बनाए गए कंपोज़ेबल यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थितियां, अक्सर rememberSaveable को सौंप दी जाती हैं. ऐसा इसलिए किया जाता है, ताकि Activity को फिर से बनाने पर भी स्थिति बनी रहे. इस तरह के फ़ंक्शन के उदाहरणों में rememberScaffoldState() और rememberLazyListState() शामिल हैं.
  • डेटा के यूज़र इंटरफ़ेस (यूआई) स्कोप वाले सोर्स के रेफ़रंस मौजूद हैं: डेटा के सोर्स, जैसे कि लाइफ़साइकल एपीआई और रिसॉर्स को सुरक्षित तरीके से रेफ़रंस किया जा सकता है और पढ़ा जा सकता है. ऐसा इसलिए, क्योंकि यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर का लाइफ़साइकल, यूज़र इंटरफ़ेस (यूआई) के जैसा ही होता है.
  • इसे कई यूज़र इंटरफ़ेस (यूआई) में फिर से इस्तेमाल किया जा सकता है: एक ही यूआई लॉजिक स्टेट होल्डर के अलग-अलग इंस्टेंस को ऐप्लिकेशन के अलग-अलग हिस्सों में फिर से इस्तेमाल किया जा सकता है. उदाहरण के लिए, किसी चिप ग्रुप के लिए उपयोगकर्ता के इनपुट इवेंट मैनेज करने वाले स्टेट होल्डर का इस्तेमाल, फ़िल्टर चिप के लिए खोज पेज पर किया जा सकता है. साथ ही, ईमेल पाने वालों के "इनको" फ़ील्ड के लिए भी इसका इस्तेमाल किया जा सकता है.

यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर को आम तौर पर, सामान्य क्लास के साथ लागू किया जाता है. ऐसा इसलिए है, क्योंकि यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर को बनाने की ज़िम्मेदारी यूज़र इंटरफ़ेस (यूआई) की होती है. साथ ही, यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर का लाइफ़साइकल, यूज़र इंटरफ़ेस (यूआई) के लाइफ़साइकल के जैसा ही होता है. उदाहरण के लिए, Jetpack Compose में स्टेट होल्डर, कंपोज़िशन का हिस्सा होता है और कंपोज़िशन के लाइफ़साइकल को फ़ॉलो करता है.

ऊपर दी गई जानकारी को Now in Android के सैंपल में दिए गए इस उदाहरण से समझा जा सकता है:

Now in Android में, यूज़र इंटरफ़ेस (यूआई) लॉजिक को मैनेज करने के लिए, सामान्य क्लास स्टेट होल्डर का इस्तेमाल किया जाता है
पांचवीं इमेज. Now in Android का सैंपल ऐप्लिकेशन.

Now in Android सैंपल में, डिवाइस की स्क्रीन के साइज़ के हिसाब से नेविगेशन के लिए बॉटम ऐप्लिकेशन बार या नेविगेशन रेल दिखाई जाती है. छोटी स्क्रीन पर बॉटम ऐप्लिकेशन बार और बड़ी स्क्रीन पर नेविगेशन रेल का इस्तेमाल किया जाता है.

NiaApp कंपोज़ेबल फ़ंक्शन में इस्तेमाल किए गए सही नेविगेशन यूज़र इंटरफ़ेस (यूआई) एलिमेंट का फ़ैसला करने का लॉजिक, कारोबार के लॉजिक पर निर्भर नहीं करता. इसलिए, इसे NiaAppState नाम के सामान्य क्लास स्टेट होल्डर से मैनेज किया जा सकता है:

@Stable
class NiaAppState(
    val navController: NavHostController,
    val windowSizeClass: WindowSizeClass
) {

    // UI logic
    val shouldShowBottomBar: Boolean
        get() = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact ||
            windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact

    // UI logic
    val shouldShowNavRail: Boolean
        get() = !shouldShowBottomBar

   // UI State
    val currentDestination: NavDestination?
        @Composable get() = navController
            .currentBackStackEntryAsState().value?.destination

    // UI logic
    fun navigate(destination: NiaNavigationDestination, route: String? = null) { /* ... */ }

     /* ... */
}

ऊपर दिए गए उदाहरण में, NiaAppState के बारे में यहां दी गई जानकारी ध्यान देने लायक है:

  • Activity को फिर से बनाने पर भी बना रहता है: NiaAppState, कंपोज़िशन में remembered होता है. इसे कंपोज़ेबल फ़ंक्शन rememberNiaAppState के साथ बनाया जाता है. यह Compose के नाम रखने के नियमों का पालन करता है. Activity को फिर से बनाने के बाद, पिछला इंस्टेंस मिट जाता है और एक नया इंस्टेंस बन जाता है. इसमें सभी डिपेंडेंसी पास की जाती हैं, जो फिर से बनाए गए Activity के नए कॉन्फ़िगरेशन के लिए सही होती हैं. ये डिपेंडेंसी नई हो सकती हैं या पिछले कॉन्फ़िगरेशन से वापस लाई गई हो सकती हैं. उदाहरण के लिए, rememberNavController() का इस्तेमाल NiaAppState कंस्ट्रक्टर में किया जाता है. यह Activity को फिर से बनाने के दौरान, स्थिति को बनाए रखने के लिए rememberSaveable को सौंपता है.
  • डेटा के यूज़र इंटरफ़ेस (यूआई) स्कोप वाले सोर्स के रेफ़रंस मौजूद हैं: navigationController, Resources, और लाइफ़साइकल के स्कोप वाले अन्य मिलते-जुलते टाइप के रेफ़रंस को NiaAppState में सुरक्षित तरीके से रखा जा सकता है, क्योंकि ये सभी एक ही लाइफ़साइकल स्कोप शेयर करते हैं.

स्टेट होल्डर के लिए, ViewModel और सामान्य क्लास में से किसी एक को चुनना

पिछले सेक्शन में, ViewModel और सामान्य क्लास स्टेट होल्डर में से किसी एक को चुनने का फ़ैसला, यूज़र इंटरफ़ेस (यूआई) की स्थिति पर लागू होने वाले लॉजिक और उस लॉजिक के लिए इस्तेमाल किए जाने वाले डेटा के सोर्स के आधार पर लिया जाता है.

संक्षेप में, इस डायग्राम में यूज़र इंटरफ़ेस (यूआई) में स्टेट होल्डर की पोज़िशन दिखाई गई है. साथ ही, स्टेट प्रोडक्शन पाइपलाइन के बारे में बताया गया है:

डेटा, डेटा तैयार करने वाली लेयर से यूज़र इंटरफ़ेस (यूआई) लेयर तक जाता है
छठी इमेज. यूज़र इंटरफ़ेस (यूआई) की स्थिति को मैनेज करने वाली प्रोडक्शन पाइपलाइन में स्टेट होल्डर. तीरों का मतलब डेटा फ़्लो है.

आखिर में, आपको यूज़र इंटरफ़ेस (यूआई) की स्थिति को ऐसे स्टेट होल्डर का इस्तेमाल करके जनरेट करना चाहिए जो उस जगह के सबसे करीब हों जहां उनका इस्तेमाल किया जाता है. अनौपचारिक तौर पर, आपको स्टेट को कम से कम रखना चाहिए. हालांकि, आपको मालिकाना हक बनाए रखना चाहिए. अगर आपको कारोबार के लॉजिक का ऐक्सेस चाहिए और यूज़र इंटरफ़ेस (यूआई) की स्थिति को तब तक बनाए रखना है, जब तक किसी स्क्रीन पर नेविगेट किया जा सकता है, तो ViewModel आपके कारोबार के लॉजिक की स्थिति को बनाए रखने के लिए एक बेहतरीन विकल्प है. भले ही, Activity फिर से बनाया गया हो. कम समय के लिए यूज़र इंटरफ़ेस (यूआई) की स्थिति और यूज़र इंटरफ़ेस (यूआई) के लॉजिक के लिए, ऐसी सामान्य क्लास का इस्तेमाल किया जा सकता है जिसका लाइफ़साइकल सिर्फ़ यूज़र इंटरफ़ेस (यूआई) पर निर्भर करता है.

स्टेट होल्डर को एक साथ कई बार इस्तेमाल किया जा सकता है

स्टेट होल्डर, अन्य स्टेट होल्डर पर तब तक निर्भर रह सकते हैं, जब तक कि डिपेंडेंसी का लाइफ़टाइम बराबर या कम हो. इसके उदाहरण ये हैं:

  • यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर, किसी दूसरे यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर पर निर्भर हो सकता है.
  • स्क्रीन लेवल का स्टेट होल्डर, यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर पर निर्भर हो सकता है.

नीचे दिए गए कोड स्निपेट में दिखाया गया है कि Compose का DrawerState, SwipeableState नाम के किसी दूसरे इंटरनल स्टेट होल्डर पर कैसे निर्भर करता है. साथ ही, यह भी दिखाया गया है कि किसी ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) लॉजिक का स्टेट होल्डर, DrawerState पर कैसे निर्भर कर सकता है:

@Stable
class DrawerState(/* ... */) {
  internal val swipeableState = SwipeableState(/* ... */)
  // ...
}

@Stable
class MyAppState(
  private val drawerState: DrawerState,
  private val navController: NavHostController
) { /* ... */ }

@Composable
fun rememberMyAppState(
  drawerState: DrawerState = rememberDrawerState(DrawerValue.Closed),
  navController: NavHostController = rememberNavController()
): MyAppState = remember(drawerState, navController) {
  MyAppState(drawerState, navController)
}

किसी स्टेट होल्डर से ज़्यादा समय तक चलने वाली डिपेंडेंसी का उदाहरण, स्क्रीन लेवल के स्टेट होल्डर पर निर्भर रहने वाला यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर होगा. इससे, कम समय तक इस्तेमाल किए जा सकने वाले स्टेट होल्डर को फिर से इस्तेमाल करने की संभावना कम हो जाएगी. साथ ही, इसे ज़रूरत से ज़्यादा लॉजिक और स्टेट का ऐक्सेस मिल जाएगा.

अगर कम समय के लिए स्टेट होल्डर को ज़्यादा स्कोप वाले स्टेट होल्डर से कुछ जानकारी चाहिए, तो स्टेट होल्डर इंस्टेंस को पास करने के बजाय, सिर्फ़ वह जानकारी पास करें जो उसे पैरामीटर के तौर पर चाहिए. उदाहरण के लिए, यहां दिए गए कोड स्निपेट में, यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर क्लास को, ViewModel से सिर्फ़ ज़रूरी पैरामीटर मिलते हैं. इसके बजाय, पूरे ViewModel इंस्टेंस को डिपेंडेंसी के तौर पर पास नहीं किया जाता.

class MyScreenViewModel(/* ... */) {
  val uiState: StateFlow<MyScreenUiState> = /* ... */
  fun doSomething() { /* ... */ }
  fun doAnotherThing() { /* ... */ }
  // ...
}

@Stable
class MyScreenState(
  // DO NOT pass a ViewModel instance to a plain state holder class
  // private val viewModel: MyScreenViewModel,

  // Instead, pass only what it needs as a dependency
  private val someState: StateFlow<SomeState>,
  private val doSomething: () -> Unit,

  // Other UI-scoped types
  private val scaffoldState: ScaffoldState
) {
  /* ... */
}

@Composable
fun rememberMyScreenState(
  someState: StateFlow<SomeState>,
  doSomething: () -> Unit,
  scaffoldState: ScaffoldState = rememberScaffoldState()
): MyScreenState = remember(someState, doSomething, scaffoldState) {
  MyScreenState(someState, doSomething, scaffoldState)
}

@Composable
fun MyScreen(
  modifier: Modifier = Modifier,
  viewModel: MyScreenViewModel = viewModel(),
  state: MyScreenState = rememberMyScreenState(
    someState = viewModel.uiState.map { it.toSomeState() },
    doSomething = viewModel::doSomething
  ),
  // ...
) {
  /* ... */
}

इस डायग्राम में, यूज़र इंटरफ़ेस (यूआई) और पिछले कोड स्निपेट के अलग-अलग स्टेट होल्डर के बीच की डिपेंडेंसी दिखाई गई है:

यूज़र इंटरफ़ेस (यूआई), यूज़र इंटरफ़ेस (यूआई) लॉजिक स्टेट होल्डर और स्क्रीन लेवल स्टेट होल्डर, दोनों पर निर्भर करता है
सातवीं इमेज. अलग-अलग स्टेट होल्डर के हिसाब से यूज़र इंटरफ़ेस (यूआई). ऐरो का मतलब डिपेंडेंसी है.

सैंपल

यहां दिए गए Google के सैंपल से, यूज़र इंटरफ़ेस (यूआई) लेयर में स्टेट होल्डर के इस्तेमाल के बारे में पता चलता है. इन दिशा-निर्देशों को लागू करने के तरीके के बारे में जानने के लिए, इन्हें देखें: