यूज़र इंटरफ़ेस (यूआई) लेयर

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

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

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

सामान्य केस स्टडी

मान लें कि कोई ऐप्लिकेशन, किसी व्यक्ति के लिए समाचार लेखों को फ़ेच करता है, ताकि वह उन्हें पढ़ सके. इस ऐप्लिकेशन में एक लेखों वाली स्क्रीन है. इस पर पढ़ने के लिए उपलब्ध लेख दिखाए जाते हैं. साथ ही, साइन इन किए हुए उपयोगकर्ता, खास लेखों को बुकमार्क कर सकते हैं. किसी भी समय कई लेख उपलब्ध हो सकते हैं. इसलिए, पाठक के पास कैटगरी के हिसाब से लेख ब्राउज़ करने का विकल्प होना चाहिए. संक्षेप में, इस ऐप्लिकेशन की मदद से ये काम किए जा सकते हैं:

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

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

यूज़र इंटरफ़ेस (यूआई) लेयर का आर्किटेक्चर

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

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

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

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

इनमें से सबसे बुनियादी सिद्धांत, यूज़र इंटरफ़ेस (यूआई) की स्थिति की परिभाषा है.

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

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

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

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

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

data class NewsUiState(
    val isSignedIn: Boolean = false,
    val isPremium: Boolean = false,
    val newsItems: List<NewsItemUiState> = listOf(),
    val userMessages: List<Message> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    // ...
)

यूज़र इंटरफ़ेस (यूआई) की स्थिति के बारे में ज़्यादा जानने के लिए, स्टेट और Jetpack Compose लेख पढ़ें.

बदलाव न किया जा सकने की सुविधा

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

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

इस गाइड में नेमिंग कनवेंशन

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

functionality + UiState.

उदाहरण के लिए, खबरों को दिखाने वाली स्क्रीन की स्थिति को NewsUiState कहा जा सकता है. साथ ही, खबरों की सूची में मौजूद किसी खबर की स्थिति को NewsItemUiState कहा जा सकता है.

एकतरफ़ा डेटा फ़्लो का इस्तेमाल करके स्थिति मैनेज करना

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

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

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

स्टेट होल्डर

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

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

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

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

जिस पैटर्न में स्टेट नीचे की ओर और इवेंट ऊपर की ओर जाते हैं उसे एकतरफ़ा डेटा फ़्लो (यूडीएफ़) कहा जाता है. ऐप्लिकेशन के आर्किटेक्चर पर इस पैटर्न का असर इस तरह पड़ता है:

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

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

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

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

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

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

तर्क के टाइप

किसी लेख को बुकमार्क करना, कारोबारी नियम का एक उदाहरण है. ऐसा इसलिए, क्योंकि इससे आपके ऐप्लिकेशन को फ़ायदा मिलता है. इसके बारे में ज़्यादा जानने के लिए, डेटा लेयर पेज देखें. हालांकि, अलग-अलग तरह के लॉजिक होते हैं जिन्हें तय करना ज़रूरी होता है:

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

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

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

यूडीएफ़ का इस्तेमाल क्यों करना चाहिए?

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

दूसरे शब्दों में, यूडीएफ़ की मदद से ये काम किए जा सकते हैं:

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

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

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

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

class NewsViewModel(
    // ...
) : ViewModel() {

    val uiState: NewsUiState = /* ... */
}

Kotlin फ़्लो के बारे में जानने के लिए, Android पर Kotlin फ़्लो लेख पढ़ें. StateFlow को मॉनिटर किया जा सकने वाला डेटा होल्डर के तौर पर इस्तेमाल करने का तरीका जानने के लिए, Jetpack Compose में ऐडवांस स्टेट और साइड इफ़ेक्ट कोडलैब देखें.

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

UiState की स्ट्रीम बनाने का एक सामान्य तरीका यह है कि mutableStateOf प्रॉपर्टी को private set के साथ दिखाया जाए. साथ ही, ViewModel में स्थिति को बदला जा सके, लेकिन यूज़र इंटरफ़ेस (यूआई) के लिए इसे सिर्फ़ पढ़ने के लिए उपलब्ध कराया जाए.

class NewsViewModel(
    // ...
) : ViewModel() {

    var uiState by mutableStateOf(NewsUiState())
        private set

    // ...
}

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

class NewsViewModel(
    private val repository: NewsRepository,
    // ...
) : ViewModel() {

    var uiState by mutableStateOf(NewsUiState())
        private set

    private var fetchJob: Job? = null

    fun fetchArticles(category: String) {
        fetchJob?.cancel()
        fetchJob = viewModelScope.launch {
            try {
                val newsItems = repository.newsItemsForCategory(category)
                uiState = uiState.copy(newsItems = newsItems)
            } catch (ioe: IOException) {
                // Handle the error and notify the UI when appropriate.
                val messages = getMessagesFromThrowable(ioe)
                uiState = uiState.copy(userMessages = messages)
            }
        }
    }
}

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

ज़्यादा जानकारी

पिछले दिशा-निर्देशों के अलावा, यूज़र इंटरफ़ेस (यूआई) की स्थिति को दिखाने के दौरान इन बातों का ध्यान रखें:

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

    data class NewsUiState(
        val isSignedIn: Boolean = false,
        val isPremium: Boolean = false,
        val newsItems: List<NewsItemUiState> = listOf()
    )
    
    val NewsUiState.canBookmarkNews: Boolean get() = isSignedIn && isPremium

    इस एलान में, बुकमार्क बटन की दिखने की स्थिति, दो अन्य प्रॉपर्टी से मिली हुई प्रॉपर्टी है. कारोबारी नियम के ज़्यादा जटिल होने पर, एक ऐसी UiState क्लास का होना ज़रूरी हो जाता है जिसमें सभी प्रॉपर्टी तुरंत उपलब्ध हों.

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

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

    • UiState अंतर: किसी UiState ऑब्जेक्ट में जितने ज़्यादा फ़ील्ड होंगे, स्ट्रीम के किसी फ़ील्ड के अपडेट होने की वजह से स्ट्रीम के जनरेट होने की संभावना उतनी ही ज़्यादा होगी. यूज़र इंटरफ़ेस (यूआई) एलिमेंट में, यह समझने के लिए कोई डिफ़रेंसिंग मैकेनिज़्म नहीं होता कि लगातार होने वाले उत्सर्जन अलग-अलग हैं या एक जैसे. इसलिए, हर उत्सर्जन से यूज़र इंटरफ़ेस (यूआई) एलिमेंट अपडेट होता है. इसका मतलब है कि Flow एपीआई के तरीकों, जैसे कि distinctUntilChanged() का इस्तेमाल करके, समस्या को कम करना ज़रूरी हो सकता है.

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

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

यूज़र इंटरफ़ेस (यूआई) में UiState ऑब्जेक्ट की स्ट्रीम का इस्तेमाल करने के लिए, उस ऑब्ज़र्वेबल डेटा टाइप के लिए टर्मिनल ऑपरेटर का इस्तेमाल करें जिसका इस्तेमाल किया जा रहा है. उदाहरण के लिए, Kotlin फ़्लो के लिए collect() तरीके या इसके वैरिएंट का इस्तेमाल करें.

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

@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {

    val messages by conversationViewModel.messages.collectAsStateWithLifecycle()

    ConversationScreen(
        messages = messages,
        onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) }
    )
}

@Composable
private fun ConversationScreen(
    messages: List<Message>,
    onSendMessage: (Message) -> Unit
) {

    MessagesList(messages, onSendMessage)
    /* ... */
}

जारी कार्रवाइयां दिखाएं

UiState क्लास में लोडिंग की स्थितियां दिखाने का आसान तरीका, बूलियन फ़ील्ड का इस्तेमाल करना है:

data class NewsUiState(
    val isFetchingArticles: Boolean = false,
    // ...
)

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

@Composable
fun LatestNewsScreen(
    modifier: Modifier = Modifier,
    viewModel: NewsViewModel = viewModel()
) {
    Box(modifier.fillMaxSize()) {

        if (viewModel.uiState.isFetchingArticles) {
            CircularProgressIndicator(Modifier.align(Alignment.Center))
        }

        // Add other UI elements. For example, the list.
    }
}

स्क्रीन पर गड़बड़ियां दिखाओ

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

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

data class Message(val id: Long, val message: String)

data class NewsUiState(
    val userMessages: List<Message> = listOf(),
    // ...
)

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

थ्रेडिंग और एक साथ कई काम करने की सुविधा

पक्का करें कि ViewModel में किया गया सारा काम main-safe हो. इसका मतलब है कि इसे मुख्य थ्रेड से कॉल किया जा सकता है. डेटा और डोमेन लेयर, काम को किसी दूसरी थ्रेड पर ले जाने के लिए ज़िम्मेदार होती हैं.

अगर कोई ViewModel लंबे समय तक चलने वाली कार्रवाइयां करता है, तो उस लॉजिक को बैकग्राउंड थ्रेड में ले जाने की ज़िम्मेदारी भी उसी की होती है. Kotlin को-रूटीन, एक साथ कई कार्रवाइयों को मैनेज करने का बेहतरीन तरीका है. साथ ही, Jetpack Architecture Components में इनके लिए पहले से ही सहायता उपलब्ध है. Android ऐप्लिकेशन में कोरूटीन इस्तेमाल करने के बारे में ज़्यादा जानने के लिए, Android पर Kotlin कोरूटीन लेख पढ़ें.

ऐप्लिकेशन में नेविगेशन में होने वाले बदलाव, अक्सर इवेंट जैसे उत्सर्जन की वजह से होते हैं. उदाहरण के लिए, SignInViewModel क्लास के साइन इन करने के बाद, UiState में isSignedIn फ़ील्ड को true पर सेट किया जा सकता है. इन ट्रिगर का इस्तेमाल, Consume UI state सेक्शन में बताए गए ट्रिगर की तरह ही करें. हालांकि, Navigation component के लिए, इस्तेमाल करने की प्रोसेस को कुछ समय के लिए रोक दें.

यूज़र इंटरफ़ेस (यूआई) नेविगेशन के बारे में ज़्यादा जानने के लिए, नेविगेशन 3 देखें.

पृष्ठांकन

Paging library का इस्तेमाल यूज़र इंटरफ़ेस (यूआई) में किया जाता है. इसका टाइप PagingData होता है. PagingData में ऐसे आइटम होते हैं जिनमें समय के साथ बदलाव हो सकता है. दूसरे शब्दों में कहें, तो यह एक ऐसा टाइप है जिसमें बदलाव किया जा सकता है. इसलिए, इसे यूज़र इंटरफ़ेस (यूआई) की ऐसी स्थिति में न दिखाएं जिसमें बदलाव नहीं किया जा सकता. इसके बजाय, इसे ViewModel से अलग से इसकी स्ट्रीम में दिखाएं.

यहां दिए गए उदाहरण में, Paging लाइब्रेरी का Compose API दिखाया गया है:

@Composable
fun MyScreen(flow: Flow<PagingData<String>>) {
    val lazyPagingItems = flow.collectAsLazyPagingItems()
    LazyColumn {
        items(
            lazyPagingItems.itemCount,
            key = lazyPagingItems.itemKey { it }
        ) { index ->
            val item = lazyPagingItems[index]
            Text("Item is $item")
        }
    }
}

ऐनिमेशन

टॉप-लेवल नेविगेशन ट्रांज़िशन को आसानी से उपलब्ध कराने के लिए, आपको ऐनिमेशन शुरू करने से पहले, दूसरी स्क्रीन के डेटा लोड होने का इंतज़ार करना पड़ सकता है.

नेविगेशन ट्रांज़िशन के बारे में ज़्यादा जानने के लिए, Navigation 3 और Compose में शेयर किए गए एलिमेंट के ट्रांज़िशन देखें.

अन्य संसाधन

कॉन्टेंट देखता है

सैंपल

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