Android Kotlin की बुनियादी जानकारी 05.1: ViewModel और ViewModelFactory

यह कोडलैब Android Kotlin के बुनियादी कोर्स में शामिल है. अगर आप कोडलैब के क्रम में काम करते हैं, तो आपको इस कोर्स का ज़्यादा से ज़्यादा फ़ायदा मिलेगा. सभी कोर्स कोडलैब Android Kotlin से जुड़े बेसिक कोडलैब के लैंडिंग पेज पर दिए गए हैं.

शीर्षक स्क्रीन

गेम स्क्रीन

स्कोर स्क्रीन

परिचय

इस कोडलैब में, आप Android आर्किटेक्चर के किसी एक कॉम्पोनेंट, ViewModel के बारे में जानेंगे:

  • लाइफ़साइकल के हिसाब से डेटा को सेव और मैनेज करने के लिए, आप ViewModel क्लास का इस्तेमाल करते हैं. ViewModel क्लास में, डिवाइस के कॉन्फ़िगरेशन से जुड़े बदलावों, जैसे कि स्क्रीन रोटेशन और कीबोर्ड की उपलब्धता में होने वाले बदलावों का पालन किया जा सकता है.
  • आप ViewModel ऑब्जेक्ट को इंस्टैंशिएट करने और उसे लौटाने के लिए, ViewModelFactory क्लास का इस्तेमाल करते हैं, जो कॉन्फ़िगरेशन में हुए बदलावों से बचा रहता है.

आपको क्या पता होना चाहिए

  • Kotlin में बेसिक Android ऐप्लिकेशन बनाने का तरीका.
  • अपने ऐप्लिकेशन में नेविगेशन लागू करने के लिए, नेविगेशन ग्राफ़ को इस्तेमाल करने का तरीका.
  • अपने ऐप्लिकेशन के डेस्टिनेशन के बीच नेविगेट करने और नेविगेशन के डेस्टिनेशन के बीच डेटा पास करने के लिए, कोड जोड़ने का तरीका.
  • फ़्रैगमेंट और गतिविधि पूरी होने की प्रोसेस कैसे काम करती है.
  • Android Studio में लॉगकैट का इस्तेमाल करके, किसी ऐप्लिकेशन में लॉग इन की जानकारी जोड़ने और लॉग पढ़ने का तरीका.

आप इन चीज़ों के बारे में जानेंगे

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

आप क्या कर पाएंगे!

  • ऐप्लिकेशन का डेटा सेव करने के लिए ViewModel ऐप्लिकेशन में जोड़ें, ताकि डेटा कॉन्फ़िगरेशन में हुए बदलावों के दौरान बचा रहे.
  • कंस्ट्रक्टर पैरामीटर के साथ ViewModel ऑब्जेक्ट को इंस्टैंशिएट करने के लिए, ViewModelFactory और फ़ैक्ट्री-तरीका डिज़ाइन पैटर्न का इस्तेमाल करें.

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

पहला खिलाड़ी ऐप्लिकेशन में शब्दों को देखता है और हर एक को एक-एक करके करता है, ताकि यह पक्का हो सके कि वह दूसरे खिलाड़ी को शब्द न दिखाए. दूसरा खिलाड़ी, शब्द का अनुमान लगाने की कोशिश करता है.

गेम खेलने के लिए, पहला खिलाड़ी डिवाइस पर ऐप्लिकेशन खोलता है और एक शब्द देखता है, उदाहरण के लिए, " गिटार,&कोटेशन; जैसा कि नीचे स्क्रीनशॉट में दिखाया गया है.

पहला खिलाड़ी सिर्फ़ शब्द बोलता हुआ सावधान रहता है.

  • जब दूसरा खिलाड़ी शब्द का सही अनुमान लगाता है, तो पहला खिलाड़ी ठीक है बटन दबाता है. इससे बटन की संख्या एक बढ़ जाती है और अगला शब्द दिखता है.
  • अगर दूसरा खिलाड़ी शब्द का अनुमान नहीं लगा सकता, तो पहला खिलाड़ी छोड़ें बटन को दबाता है. इससे एक शब्द कम हो जाता है और अगले शब्द पर चला जाता है.
  • गेम खत्म करने के लिए, गेम खत्म करें बटन दबाएं. (यह सुविधा सीरीज़ के पहले कोडलैब (कोड बनाना सीखना) के स्टार्टर कोड में't है.)

इस टास्क में, आप स्टार्टर ऐप्लिकेशन डाउनलोड करके चलाते हैं और कोड की जांच करते हैं.

पहला चरण: शुरू करना

  1. GuessTheWord स्टार्टर कोड डाउनलोड करें और प्रोजेक्ट को Android Studio में खोलें.
  2. ऐप्लिकेशन को Android पर चलने वाले डिवाइस या एम्युलेटर पर चलाएं.
  3. बटन पर टैप करें. ध्यान दें कि अभी नहीं बटन अगले शब्द को दिखाता है और स्कोर में एक की कमी कर देता है. वहीं, ठीक है बटन अगला शब्द दिखाता है और स्कोर को एक बढ़ा देता है. गेम खत्म करें बटन लागू नहीं किया गया है, इसलिए जब आप इसे टैप करेंगे तो कुछ नहीं होगा.

दूसरा चरण: कोड कदम-दर-कदम निर्देश

  1. Android Studio में, कोड एक्सप्लोर करें और जानें कि ऐप्लिकेशन कैसे काम करता है.
  2. नीचे बताई गई फ़ाइलें देखना न भूलें, जो खास तौर पर अहम हैं.

MainActivity.kt

इस फ़ाइल में सिर्फ़ डिफ़ॉल्ट, टेंप्लेट से जनरेट किया गया कोड शामिल है.

res/layout/main_activity.xml

इस फ़ाइल में ऐप्लिकेशन का मुख्य लेआउट शामिल है. जब उपयोगकर्ता ऐप्लिकेशन में नेविगेट करता है, तब NavHostFragment दूसरे फ़्रैगमेंट को होस्ट करता है.

यूज़र इंटरफ़ेस (यूआई) फ़्रैगमेंट

स्टार्टर कोड में तीन पैकेज होते हैं. यह com.example.android.guesstheword.screens पैकेज के तीन अलग-अलग पैकेज में होते हैं:

  • शीर्षक स्क्रीन के लिए title/TitleFragment
  • गेम स्क्रीन के लिए game/GameFragment
  • स्कोर स्क्रीन के लिए score/ScoreFragment

screen/title/TitleFragment.kt

शीर्षक फ़्रैगमेंट वह पहली स्क्रीन होती है जो ऐप्लिकेशन लॉन्च होने पर दिखाई जाती है. क्लिक स्क्रीन को चलाएं बटन पर सेट करके, गेम स्क्रीन पर जाएं.

screen/game/GameFragment.kt

यह मुख्य फ़्रैगमेंट है, जिसमें ज़्यादातर गेम की कार्रवाई होती है:

  • वैरिएबल, मौजूदा शब्द और मौजूदा स्कोर के लिए तय किए जाते हैं.
  • wordList में बताए गए resetList() तरीके का इस्तेमाल, गेम में इस्तेमाल किए जाने वाले शब्दों की सैंपल सूची है.
  • onSkip() तरीका स्किप बटन के लिए क्लिक हैंडलर है. यह स्कोर को 1 तक कम कर देता है, फिर nextWord() तरीके का इस्तेमाल करके अगला शब्द दिखाता है.
  • onCorrect() तरीका, ठीक है बटन के लिए क्लिक हैंडलर है. इस तरीके को onSkip() तरीके की तरह ही लागू किया जाता है. अंतर सिर्फ़ यह है कि यह तरीका घटाने के बजाय स्कोर में 1 जोड़ता है.

screen/score/ScoreFragment.kt

ScoreFragment, गेम की फ़ाइनल स्क्रीन होती है. यहां, खिलाड़ियों का फ़ाइनल स्कोर दिखता है. इस कोडलैब में, आप इस स्क्रीन को दिखाने और आखिरी स्कोर दिखाने के लिए, इसे लागू करते हैं.

res/navigation/main_navigation.xml

नेविगेशन ग्राफ़ दिखाता है कि फ़्रैगमेंट को नेविगेशन की मदद से कैसे जोड़ा जाता है:

  • शीर्षक फ़्रैगमेंट से, उपयोगकर्ता गेम फ़्रैगमेंट पर जा सकता है.
  • गेम के फ़्रैगमेंट से, उपयोगकर्ता स्कोर फ़्रैगमेंट पर जा सकते हैं.
  • स्कोर फ़्रैगमेंट से, उपयोगकर्ता गेम फ़्रैगमेंट पर वापस नेविगेट कर सकते हैं.

इस टास्क में, आपको GuessTheWord स्टार्टर ऐप्लिकेशन के साथ समस्याएं मिलेंगी.

  1. स्टार्टर कोड चलाएं और कुछ शब्दों के बाद गेम खेलें. इसके बाद, हर शब्द के बाद अभी नहीं या ठीक है पर टैप करें.
  2. गेम स्क्रीन पर अब कोई शब्द और मौजूदा स्कोर दिखता है. डिवाइस या एम्युलेटर को घुमाकर, स्क्रीन ओरिएंटेशन में बदलाव करें. ध्यान दें कि मौजूदा स्कोर चला जाता है.
  3. कुछ और शब्दों में गेम खेलें. जब गेम स्क्रीन कुछ स्कोर के साथ दिखती है, तो ऐप्लिकेशन को बंद करें और दोबारा खोलें. ध्यान दें कि गेम शुरू से शुरू होता है, क्योंकि ऐप्लिकेशन की स्थिति सेव नहीं होती है.
  4. कुछ शब्दों की मदद से गेम खेलें. इसके बाद, गेम खत्म करें बटन पर टैप करें. ध्यान दें, कुछ नहीं होगा.

ऐप्लिकेशन में समस्याएं:

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

आप इस कोडलैब में ऐप्लिकेशन के आर्किटेक्चर कॉम्पोनेंट के इस्तेमाल से, इन समस्याओं को हल कर सकते हैं.

ऐप्लिकेशन का आर्किटेक्चर

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

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

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

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

GuessTheWord स्टार्टर कोड में, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर तीन फ़्रैगमेंट होते हैं: GameFragment, ScoreFragment,, और TitleFragment. समस्याओं को समझने के लिए डिज़ाइन किए गए मूल सिद्धांतों के कोटेशन और डिज़ाइन के आधार पर, GameFragment को सिर्फ़ स्क्रीन पर मौजूद गेम के एलिमेंट के बारे में बताने और उपयोगकर्ता के बटन पर टैप करने के साथ-साथ कुछ और जानने की ज़िम्मेदारी है. जब उपयोगकर्ता किसी बटन पर टैप करता है, तो यह जानकारी GameViewModel को भेज दी जाती है.

ViewModel

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

GameViewModel में स्कोर का मान, शब्दों की सूची, और मौजूदा शब्द जैसा डेटा होता है, क्योंकि यह स्क्रीन पर दिखाया जाने वाला डेटा है. GameViewModel में कारोबार की समझ भी शामिल होती है. इसकी मदद से, यह तय किया जा सकता है कि डेटा की मौजूदा स्थिति क्या है.

ViewModelFactory

ViewModelFactory कंस्ट्रक्टर पैरामीटर के साथ या उसके बिना, ViewModel ऑब्जेक्ट को इंस्टैंशिएट करता है.

बाद के कोडलैब में, आप यूज़र इंटरफ़ेस (यूआई) कंट्रोलर और ViewModel से जुड़े अन्य Android आर्किटेक्चर के कॉम्पोनेंट के बारे में जानते हैं.

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

इस टास्क में, आप अपने पहले ViewModel को ऐप्लिकेशन में जोड़ते हैं, GameFragment को GameViewModel के लिए. आपको यह भी पता चलेगा कि इसका मतलब क्या है और ViewModel, लाइफ़साइकल की जानकारी देता है.

पहला चरण: GameViewModel क्लास जोड़ना

  1. build.gradle(module:app) फ़ाइल खोलें. dependencies ब्लॉक के अंदर, ViewModel के लिए, Gradle डिपेंडेंसी जोड़ें.

    अगर लाइब्रेरी के नए वर्शन का इस्तेमाल किया जा रहा है, तो समाधान वाले ऐप्लिकेशन को उम्मीद के मुताबिक कंपाइल किया जाना चाहिए. अगर इससे समस्या हल नहीं होती है, तो समस्या को हल करने की कोशिश करें या नीचे दिए गए वर्शन पर वापस जाएं.
//ViewModel
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
  1. पैकेज screens/game/ फ़ोल्डर में, GameViewModel नाम की एक नई Kotlin क्लास बनाएं.
  2. GameViewModel क्लास को एब्सट्रैक्ट क्लास ViewModel बढ़ाने की अनुमति दें.
  3. यह समझने में आपकी मदद करने के लिए कि ViewModel लाइफ़साइकल की जानकारी कैसे है, log स्टेटमेंट के साथ init ब्लॉक जोड़ें.
class GameViewModel : ViewModel() {
   init {
       Log.i("GameViewModel", "GameViewModel created!")
   }
}

दूसरा चरण: onCleared() को ओवरराइड करना और लॉगिंग जोड़ें

जब उससे जुड़ा फ़्रैगमेंट अलग हो जाता है या गतिविधि पूरी हो जाती है, तो ViewModel खत्म हो जाता है. ViewModel को नुकसान पहुंचाने से ठीक पहले, संसाधनों को खाली करने के लिए onCleared() कॉलबैक को कॉल किया जाता है.

  1. GameViewModel क्लास में, onCleared() तरीके को बदलें.
  2. GameViewModel की लाइफ़साइकल को ट्रैक करने के लिए, onCleared() में लॉग स्टेटमेंट जोड़ें.
override fun onCleared() {
   super.onCleared()
   Log.i("GameViewModel", "GameViewModel destroyed!")
}

तीसरा चरण: GameViewModel को गेम फ़्रैगमेंट से जोड़ना

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

इस चरण में, आप उससे जुड़े यूज़र इंटरफ़ेस (यूआई) कंट्रोलर के GameViewModel का रेफ़रंस देते हैं, जो GameFragment है.

  1. GameFragment क्लास में, क्लास वैरिएबल के तौर पर सबसे ऊपर के लेवल पर GameViewModel टाइप का फ़ील्ड जोड़ें.
private lateinit var viewModel: GameViewModel

चौथा चरण: ViewModel शुरू करना

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

ViewModelProvider कैसे काम करता है:

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

ViewModelProvider बनाने के लिए, ViewModelProviders.of() वाले तरीके का इस्तेमाल करके, ViewModel को शुरू करें:

  1. GameFragment क्लास में, viewModel वैरिएबल शुरू करें. बाइंडिंग वैरिएबल की परिभाषा के बाद, इस कोड को onCreateView() में डालें. ViewModelProviders.of() वाले तरीके का इस्तेमाल करें और GameFragment कॉन्टेक्स्ट और GameViewModel क्लास में पास करें.
  2. ViewModel ऑब्जेक्ट के शुरू होने से ऊपर, ViewModelProviders.of() तरीके का कॉल लॉग करने के लिए एक लॉग स्टेटमेंट जोड़ें.
Log.i("GameFragment", "Called ViewModelProviders.of")
viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)
  1. ऐप्लिकेशन चलाएं. Android Studio में, लॉगकैट पैनल खोलें और Game पर फ़िल्टर करें. अपने डिवाइस या एम्युलेटर पर चलाएं बटन पर टैप करें. गेम स्क्रीन खुलती है.

    जैसा कि लॉगकैट में दिखाया गया है, GameViewModel बनाने के लिए GameFragment का onCreateView() तरीका, ViewModelProviders.of() तरीके को कॉल करता है. GameFragment और GameViewModel में जोड़े गए लॉगिंग स्टेटमेंट, लॉगकैट में दिखते हैं.

  1. अपने डिवाइस या एम्युलेटर पर ऑटो-रोटेट की सेटिंग चालू करें. साथ ही, स्क्रीन की दिशा को कई बार बदलें. GameFragment को हर बार बंद किया जाता है और फिर से बनाया जाता है, इसलिए हर बार ViewModelProviders.of() को कॉल किया जाता है. हालांकि, GameViewModel को सिर्फ़ एक बार बनाया जाता है. साथ ही, इसे हर कॉल के लिए न तो बनाया जाता है और न ही खत्म किया जाता है.
I/GameFragment: Called ViewModelProviders.of
I/GameViewModel: GameViewModel created!
I/GameFragment: Called ViewModelProviders.of
I/GameFragment: Called ViewModelProviders.of
I/GameFragment: Called ViewModelProviders.of
  1. गेम से बाहर निकलें या गेम के फ़्रैगमेंट से बाहर नेविगेट करें. GameFragment खत्म कर दिया गया है. इससे जुड़े GameViewModel को भी बंद कर दिया जाता है और कॉलबैक onCleared() को कॉल किया जाता है.
I/GameFragment: Called ViewModelProviders.of
I/GameViewModel: GameViewModel created!
I/GameFragment: Called ViewModelProviders.of
I/GameFragment: Called ViewModelProviders.of
I/GameFragment: Called ViewModelProviders.of
I/GameViewModel: GameViewModel destroyed!

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

  • ViewModel में स्क्रीन पर डेटा दिखाएं और उसे प्रोसेस करने के लिए कोड डालें.
  • ViewModel में फ़्रैगमेंट, गतिविधियां या व्यू के रेफ़रंस कभी नहीं होने चाहिए, क्योंकि गतिविधियां, फ़्रैगमेंट, और व्यू कॉन्फ़िगरेशन में होने वाले बदलावों से बचे नहीं होते.

तुलना के लिए, ViewModel को जोड़ने से पहले और ViewModel जोड़ने के बाद, यहां दिया गया है कि GameFragment यूज़र इंटरफ़ेस (यूआई) डेटा का इस्तेमाल स्टार्टर ऐप्लिकेशन में कैसे किया जाता है:

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

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

कदम 1: डेटा फ़ील्ड और डेटा प्रोसेसिंग को ViewModel में ले जाना

इन डेटा फ़ील्ड और तरीकों को GameFragment से GameViewModel में ले जाएं:

  1. word, score, और wordList डेटा फ़ील्ड को एक जगह से दूसरी जगह ले जाएं. पक्का करें कि word और score private न हों.

    बाइंडिंग वैरिएबल, GameFragmentBinding की जगह न बदलें, क्योंकि इसमें व्यू का रेफ़रंस शामिल है. इस वैरिएबल का इस्तेमाल लेआउट को इनफ़्लेट करने, क्लिक लिसनर को सेट अप करने, और स्क्रीन पर डेटा दिखाने के लिए किया जाता है—फ़्रैगमेंट की ज़िम्मेदारियां.
  2. resetList() और nextWord() मैथड को एक से दूसरी जगह ले जाएं. इन तरीकों से तय होता है कि स्क्रीन पर कौनसा शब्द दिखाया जाएगा.
  3. onCreateView() तरीके के अंदर, मेथड कॉल को resetList() और nextWord() में GameViewModel के init ब्लॉक में ले जाएं.

    ये तरीके init ब्लॉक में होने चाहिए, क्योंकि जब ViewModel बनाया जाता है, तब आपको शब्द सूची रीसेट करनी चाहिए, न कि हर बार फ़्रैगमेंट बनाने पर. आप GameFragment के init ब्लॉक में लॉग स्टेटमेंट मिटा सकते हैं.

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

अभी के लिए, दोनों जगहों पर एक जैसे तरीके डालें:

  1. onSkip() और onCorrect() तरीकों को GameFragment से GameViewModel में कॉपी करें.
  2. GameViewModel में, पक्का करें कि onSkip() और onCorrect() मेथड private न हों, क्योंकि आप फ़्रैगमेंट से इन तरीकों का रेफ़रंस देंगे.

यह GameViewModel फ़ैक्टर के लिए कोड है, जिसे रीफ़ैक्टर करके:

class GameViewModel : ViewModel() {
   // The current word
   var word = ""
   // The current score
   var score = 0
   // The list of words - the front of the list is the next word to guess
   private lateinit var wordList: MutableList<String>

   /**
    * Resets the list of words and randomizes the order
    */
   private fun resetList() {
       wordList = mutableListOf(
               "queen",
               "hospital",
               "basketball",
               "cat",
               "change",
               "snail",
               "soup",
               "calendar",
               "sad",
               "desk",
               "guitar",
               "home",
               "railway",
               "zebra",
               "jelly",
               "car",
               "crow",
               "trade",
               "bag",
               "roll",
               "bubble"
       )
       wordList.shuffle()
   }

   init {
       resetList()
       nextWord()
       Log.i("GameViewModel", "GameViewModel created!")
   }
   /**
    * Moves to the next word in the list
    */
   private fun nextWord() {
       if (!wordList.isEmpty()) {
           //Select and remove a word from the list
           word = wordList.removeAt(0)
       }
       updateWordText()
       updateScoreText()
   }
 /** Methods for buttons presses **/
   fun onSkip() {
       if (!wordList.isEmpty()) {
           score--
       }
       nextWord()
   }

   fun onCorrect() {
       if (!wordList.isEmpty()) {
           score++
       }
       nextWord()
   }

   override fun onCleared() {
       super.onCleared()
       Log.i("GameViewModel", "GameViewModel destroyed!")
   }
}

यह GameFragment को ध्यान में रखते हुए कोड दिया गया है.

/**
* Fragment where the game is played
*/
class GameFragment : Fragment() {


   private lateinit var binding: GameFragmentBinding


   private lateinit var viewModel: GameViewModel


   override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                             savedInstanceState: Bundle?): View? {

       // Inflate view and obtain an instance of the binding class
       binding = DataBindingUtil.inflate(
               inflater,
               R.layout.game_fragment,
               container,
               false
       )

       Log.i("GameFragment", "Called ViewModelProviders.of")
       viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)

       binding.correctButton.setOnClickListener { onCorrect() }
       binding.skipButton.setOnClickListener { onSkip() }
       updateScoreText()
       updateWordText()
       return binding.root

   }


   /** Methods for button click handlers **/

   private fun onSkip() {
       if (!wordList.isEmpty()) {
           score--
       }
       nextWord()
   }

   private fun onCorrect() {
       if (!wordList.isEmpty()) {
           score++
       }
       nextWord()
   }


   /** Methods for updating the UI **/

   private fun updateWordText() {
       binding.wordText.text = word
   }

   private fun updateScoreText() {
       binding.scoreText.text = score.toString()
   }
}

दूसरा चरण: GameFragment में क्लिक हैंडलर और डेटा फ़ील्ड के रेफ़रंस अपडेट करना

  1. GameFragment में, onSkip() और onCorrect() तरीके अपडेट करें. स्कोर को अपडेट करने के लिए कोड को हटाएं और इसके बजाय viewModel पर उससे जुड़े onSkip() और onCorrect() तरीकों को कॉल करें.
  2. आपने nextWord() का तरीका ViewModel में बदला है, इसलिए गेम फ़्रैगमेंट इसे ऐक्सेस नहीं कर सकता.

    GameFragment के onSkip() और onCorrect() वाले तरीके में, कॉल को updateScoreText() और updateWordText() से बदलें. इन तरीकों से स्क्रीन पर डेटा दिखता है.
private fun onSkip() {
   viewModel.onSkip()
   updateWordText()
   updateScoreText()
}
private fun onCorrect() {
   viewModel.onCorrect()
   updateScoreText()
   updateWordText()
}
  1. GameFragment में, GameViewModel वैरिएबल का इस्तेमाल करने के लिए score और word वैरिएबल अपडेट करें, क्योंकि ये वैरिएबल अब GameViewModel में हैं.
private fun updateWordText() {
   binding.wordText.text = viewModel.word
}

private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.toString()
}
  1. GameViewModel में, nextWord() तरीके के अंदर से, updateWordText() और updateScoreText() तरीकों में से कॉल हटाएं. अब इन तरीकों से GameFragment से कॉल किया जा रहा है.
  2. ऐप्लिकेशन बनाएं और पक्का करें कि उसमें कोई गड़बड़ी न हो. अगर आपको कोई गड़बड़ी मिलती है, तो प्रोजेक्ट को साफ़ करके फिर से बनाएं.
  3. ऐप्लिकेशन चलाएं और कुछ शब्दों के साथ गेम खेलें. गेम स्क्रीन पर रहते हुए, डिवाइस को घुमाएं. ध्यान दें कि ओरिएंटेशन बदलने के बाद मौजूदा स्कोर और मौजूदा शब्द को बनाए रखा जाता है.

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

इस टास्क में, आप गेम खत्म करें बटन के लिए क्लिक लिसनर लागू करते हैं.

  1. GameFragment नाम में onEndGame() नाम का एक तरीका जोड़ें. जब उपयोगकर्ता गेम खत्म करें बटन पर टैप करेगा, तब onEndGame() तरीके को कॉल किया जाएगा.
private fun onEndGame() {
   }
  1. GameFragment में, onCreateView() वाले तरीके में वह कोड ढूंढें जो ठीक है और छोड़ें बटन के लिए, क्लिक लिसनर सेट करता है. इन दो लाइनों के ठीक नीचे, गेम खत्म करें बटन के लिए एक क्लिक लिसनर सेट करें. बाइंडिंग वैरिएबल, binding का इस्तेमाल करें. क्लिक लिसनर के अंदर, onEndGame() तरीके को कॉल करें.
binding.endGameButton.setOnClickListener { onEndGame() }
  1. GameFragment में, ऐप्लिकेशन को स्कोर स्क्रीन पर नेविगेट करने के लिए gameFinished() नाम का तरीका जोड़ें. Safe Args का इस्तेमाल करके, स्कोर को तर्क के तौर पर पास करें.
/**
* Called when the game is finished
*/
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score
   NavHostFragment.findNavController(this).navigate(action)
}
  1. onEndGame() तरीके में, gameFinished() तरीके पर कॉल करें.
private fun onEndGame() {
   gameFinished()
}
  1. ऐप्लिकेशन चलाएं, गेम खेलें, और कुछ शब्दों के बीच साइकल चलाएं. गेम खत्म करें बटन पर टैप करें. ध्यान दें कि ऐप्लिकेशन, स्कोर स्क्रीन पर जाता है. हालांकि, फ़ाइनल स्कोर नहीं दिखता. आप इसे अगले टास्क में ठीक कर देते हैं.

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

फ़ैक्ट्री मेथड एक क्रिएटिव डिज़ाइन पैटर्न है, जिसमें ऑब्जेक्ट बनाने के लिए फ़ैक्ट्री के तरीकों का इस्तेमाल किया जाता है. फ़ैक्ट्री तरीका एक ऐसा तरीका है जिससे एक ही क्लास का इंस्टेंस मिलता है.

इस टास्क में, आप स्कोर फ़्रैगमेंट के लिए पैरामीटर वाले कंस्ट्रक्टर और ViewModel को इंस्टैंशिएट करने के लिए, फ़ैक्ट्री तरीके का इस्तेमाल करके ViewModel बनाते हैं.

  1. score पैकेज में, ScoreViewModel नाम की एक नई Kotlin क्लास बनाएं. स्कोर फ़्रैगमेंट के लिए यह क्लास ViewModel होगी.
  2. ScoreViewModel क्लास को ViewModel. से बढ़ाकर, फ़ाइनल स्कोर के लिए कंस्ट्रक्टर पैरामीटर जोड़ें. init ब्लॉक को लॉग स्टेटमेंट के साथ जोड़ें.
  3. ScoreViewModel क्लास में, फ़ाइनल स्कोर सेव करने के लिए, score नाम का एक वैरिएबल जोड़ें.
class ScoreViewModel(finalScore: Int) : ViewModel() {
   // The final score
   var score = finalScore
   init {
       Log.i("ScoreViewModel", "Final score is $finalScore")
   }
}
  1. score पैकेज में जाकर, ScoreViewModelFactory नाम की एक और Kotlin क्लास बनाएं. इस क्लास की ज़िम्मेदारी ScoreViewModel ऑब्जेक्ट को इंस्टैंशिएट करने की होगी.
  2. ScoreViewModelFactory क्लास को ViewModelProvider.Factory से बढ़ाएं. फ़ाइनल स्कोर के लिए कंस्ट्रक्टर पैरामीटर जोड़ें.
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {
}
  1. ScoreViewModelFactory में, Android Studio ऐसे एलिमेंट के बारे में गड़बड़ी दिखाता है जिसे लागू नहीं किया गया है. गड़बड़ी को ठीक करने के लिए, create() के तरीके को बदलें. create() तरीके में, नया बनाया गया ScoreViewModel ऑब्जेक्ट दिखाएं.
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
   if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
       return ScoreViewModel(finalScore) as T
   }
   throw IllegalArgumentException("Unknown ViewModel class")
}
  1. ScoreFragment में, ScoreViewModel और ScoreViewModelFactory के लिए क्लास वैरिएबल बनाएं.
private lateinit var viewModel: ScoreViewModel
private lateinit var viewModelFactory: ScoreViewModelFactory
  1. ScoreFragment में, onCreateView() वैरिएबल के अंदर, binding वैरिएबल की शुरुआत करने के बाद, viewModelFactory को शुरू करें. ScoreViewModelFactory का इस्तेमाल करें. आर्ग्युमेंट बंडल से फ़ाइनल स्कोर में कंस्ट्रक्टर पैरामीटर के तौर पर ScoreViewModelFactory() को पास करें.
viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score)
  1. onCreateView( में, viewModelFactory शुरू करने के बाद, viewModel ऑब्जेक्ट शुरू करें. ViewModelProviders.of() तरीके को कॉल करें और उससे जुड़े स्कोर फ़्रैगमेंट का संदर्भ पास करें और viewModelFactory. इससे viewModelFactory क्लास. में बताए गए तरीके से ScoreViewModel ऑब्जेक्ट बन जाएगा
viewModel = ViewModelProviders.of(this, viewModelFactory)
       .get(ScoreViewModel::class.java)
  1. onCreateView() तरीके में, viewModel शुरू करने के बाद, scoreText व्यू के टेक्स्ट को ScoreViewModel में बताए गए फ़ाइनल स्कोर पर सेट करें.
binding.scoreText.text = viewModel.score.toString()
  1. अपना ऐप्लिकेशन चलाएं और गेम खेलें. कुछ या सभी शब्दों पर जाने के बाद, गेम खत्म करें पर टैप करें. ध्यान दें कि स्कोर फ़्रैगमेंट अब फ़ाइनल स्कोर दिखाता है.

  1. ज़रूरी नहीं: ScoreViewModel पर फ़िल्टर करके, लॉगकैट में ScoreViewModel लॉग देखें. स्कोर की वैल्यू दिखनी चाहिए.
2019-02-07 10:50:18.328 com.example.android.guesstheword I/ScoreViewModel: Final score is 15

इस टास्क में, ViewModel का इस्तेमाल करने के लिए आपने ScoreFragment लागू किया. आपने ViewModelFactory इंटरफ़ेस का इस्तेमाल करके, ViewModel के लिए पैरामीटर वाला कंस्ट्रक्टर बनाने का तरीका भी सीखा.

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

Android Studio प्रोजेक्ट: GuessTheWord

  • Android ऐप्लिकेशन आर्किटेक्चर के दिशा-निर्देशों से, अलग-अलग ज़िम्मेदारियों वाली कक्षाओं को अलग करने का सुझाव मिलता है.
  • यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, यूज़र इंटरफ़ेस (यूआई) पर आधारित क्लास होता है, जैसे कि Activity या Fragment. यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में सिर्फ़ वह लॉजिक होना चाहिए जो यूज़र इंटरफ़ेस (यूआई) और ऑपरेटिंग सिस्टम के इंटरैक्शन को मैनेज करता हो; यूज़र इंटरफ़ेस (यूआई) में दिखाने के लिए डेटा शामिल नहीं होना चाहिए. उस डेटा को ViewModel में डालें.
  • ViewModel क्लास, यूज़र इंटरफ़ेस (यूआई) से जुड़ा डेटा सेव और मैनेज करता है. ViewModel क्लास, डेटा रोटेशन जैसे कॉन्फ़िगरेशन बदलावों से बच सकती है.
  • ViewModel, सुझाए गए Android आर्किटेक्चर कॉम्पोनेंट में से एक है.
  • ViewModelProvider.Factory एक इंटरफ़ेस है, जिसका इस्तेमाल करके आप ViewModel ऑब्जेक्ट बना सकते हैं.

नीचे दी गई टेबल, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर की तुलना ViewModel इंस्टेंस से करती है जिनमें उनका डेटा होता है:

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

ViewModel

यूज़र इंटरफ़ेस (यूआई) कंट्रोलर का उदाहरण ScoreFragment है, जिसे आपने इस कोडलैब में बनाया है.

इस कोडलैब में बनाया गया ViewModel, ScoreViewModel का उदाहरण है.

इसमें यूज़र इंटरफ़ेस (यूआई) में दिखाया जाने वाला डेटा शामिल नहीं है.

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

इसमें डेटा दिखाने के लिए कोड और क्लिक लिसनर जैसे उपयोगकर्ता इवेंट कोड शामिल होते हैं.

डेटा प्रोसेसिंग के लिए कोड शामिल है.

हर कॉन्फ़िगरेशन बदलाव के दौरान खत्म की गई और फिर से बनाई गई.

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

इसमें व्यू शामिल हैं.

इसमें गतिविधियों, फ़्रैगमेंट या व्यू का रेफ़रंस कभी नहीं होना चाहिए, क्योंकि ये कॉन्फ़िगरेशन में हुए बदलावों में बदलाव नहीं करते. हालांकि, ViewModel में ऐसा होता है.

इसमें, ViewModel से जुड़ी जानकारी शामिल है.

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

Udcity कोर्स:

Android डेवलपर दस्तावेज़:

अन्य:

इस सेक्शन में उन छात्र-छात्राओं के लिए गृहकार्य की असाइनमेंट की सूची दी गई है जो इस कोडलैब के ज़रिए एक शिक्षक की देखरेख में कोर्स में काम कर रहे हैं. यह क्रिएटर का काम #33 पर निर्भर करता है:

  • अगर ज़रूरी हो, तो होमवर्क असाइन करें.
  • छात्र-छात्राओं को होमवर्क के असाइनमेंट सबमिट करने के तरीके के बारे में बताएं.
  • होमवर्क असाइनमेंट को ग्रेड दें.

शिक्षक इन सुझावों का इस्तेमाल जितनी चाहें उतनी कम या ज़्यादा कर सकते हैं. साथ ही, उन्हें अपने हिसाब से कोई भी होमवर्क असाइन करना चाहिए.

अगर आप इस कोडलैब के ज़रिए खुद काम कर रहे हैं, तो बेझिझक इन होमवर्क असाइनमेंट का इस्तेमाल करें.

इन सवालों के जवाब दें

पहला सवाल

डिवाइस-कॉन्फ़िगरेशन में बदलाव के दौरान डेटा खोने से बचने के लिए, क्या आपको ऐप्लिकेशन डेटा को किस कक्षा में सेव करना चाहिए?

  • ViewModel
  • LiveData
  • Fragment
  • Activity

दूसरा सवाल

ViewModel में फ़्रैगमेंट, गतिविधियों या व्यू के रेफ़रंस कभी नहीं होने चाहिए. सही या गलत?

  • सही
  • गलत

तीसरा सवाल

ViewModel कब खत्म होता है?

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

चौथा सवाल

ViewModelFactory इंटरफ़ेस किस लिए है?

  • ViewModel ऑब्जेक्ट इंस्टैंशिएट करें.
  • ओरिएंटेशन में बदलाव के दौरान डेटा बनाए रखना.
  • स्क्रीन पर दिखाया जा रहा डेटा रीफ़्रेश किया जा रहा है.
  • ऐप्लिकेशन डेटा बदलने पर सूचनाएं पाना.

अगला लेसन शुरू करें: 5.2: LiveData और LiveData ऑब्ज़र्वर

इस कोर्स में दिए गए दूसरे कोडलैब के लिंक के लिए, Android Kotlin की बुनियादी बातें कोडलैब का लैंडिंग पेज देखें.