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

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

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

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

एक बुनियादी केस स्टडी

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

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

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

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

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

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

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

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

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

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

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

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

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

इस केस स्टडी पर गौर करें: समाचार ऐप्लिकेशन की ज़रूरी शर्तों को पूरा करने के लिए, यूज़र इंटरफ़ेस (यूआई) को पूरी तरह से रेंडर करने के लिए ज़रूरी जानकारी को 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 State की गाइड देखें.

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

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

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

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

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

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

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

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

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

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

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

UiState की स्ट्रीम बनाने का एक सामान्य तरीका है, private set के साथ mutableStateOf प्रॉपर्टी को एक्सपोज़ करना. इससे 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 में किया गया सारा काम मेन-सेफ़ हो. इसका मतलब है कि उसे मुख्य थ्रेड से कॉल करना सुरक्षित हो. डेटा और डोमेन लेयर, काम को किसी दूसरे थ्रेड पर ले जाने के लिए ज़िम्मेदार होती हैं.

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

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

यूज़र इंटरफ़ेस (यूआई) नेविगेशन के बारे में ज़्यादा जानने के लिए, नेविगेशन 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")
        }
    }
}

ऐनिमेशन

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

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

अन्य संसाधन

कॉन्टेंट देखना

सैंपल

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