Android Kotlin Fundamentals 05.2: LiveData और LiveData ऑब्ज़र्वर

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

परिचय

पिछले कोडलैब में, आपने GuessTheWord ऐप्लिकेशन में ViewModel का इस्तेमाल किया था, ताकि डिवाइस के कॉन्फ़िगरेशन में बदलाव होने पर भी ऐप्लिकेशन का डेटा सुरक्षित रहे. इस कोडलैब में, आपको ViewModel क्लास में मौजूद डेटा के साथ LiveData को इंटिग्रेट करने का तरीका बताया गया है. LiveData, Android आर्किटेक्चर कॉम्पोनेंट में से एक है. इसकी मदद से, ऐसे डेटा ऑब्जेक्ट बनाए जा सकते हैं जो डेटाबेस में बदलाव होने पर व्यू को सूचना देते हैं.

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

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

  • Kotlin में बुनियादी Android ऐप्लिकेशन बनाने का तरीका.
  • अपने ऐप्लिकेशन के डेस्टिनेशन के बीच नेविगेट करने का तरीका.
  • ऐक्टिविटी और फ़्रैगमेंट लाइफ़साइकल.
  • अपने ऐप्लिकेशन में ViewModel ऑब्जेक्ट इस्तेमाल करने का तरीका.
  • ViewModelProvider.Factory इंटरफ़ेस का इस्तेमाल करके, ViewModel ऑब्जेक्ट बनाने का तरीका.

आपको क्या सीखने को मिलेगा

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

आपको क्या करना होगा

  • GuessTheWord ऐप्लिकेशन में शब्द और स्कोर के लिए, LiveData का इस्तेमाल करें.
  • ऐसे ऑब्ज़र्वर जोड़ें जो शब्द या स्कोर में बदलाव होने पर सूचना दें.
  • बदली गई वैल्यू दिखाने वाले टेक्स्ट व्यू अपडेट करें.
  • गेम खत्म होने का इवेंट जोड़ने के लिए, LiveData ऑब्ज़र्वर पैटर्न का इस्तेमाल करें.
  • फिर से चलाएं बटन को लागू करें.

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

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

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

पहला खिलाड़ी, शब्द को बोलकर नहीं, बल्कि ऐक्टिंग करके बताता है.

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

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

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

गेम की स्क्रीन

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

इस टास्क में, आपको इस कोडलैब के लिए स्टार्टर कोड ढूंढना और उसे चलाना है. पिछले कोडलैब में बनाए गए GuessTheWord ऐप्लिकेशन को स्टार्टर कोड के तौर पर इस्तेमाल किया जा सकता है. इसके अलावा, स्टार्टर ऐप्लिकेशन भी डाउनलोड किया जा सकता है.

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

LiveData, ऑब्ज़र्व की जा सकने वाली डेटा होल्डर क्लास है. यह लाइफ़साइकल के बारे में जानती है. उदाहरण के लिए, GuessTheWord ऐप्लिकेशन में मौजूदा स्कोर के चारों ओर LiveData रैप किया जा सकता है. इस कोडलैब में, आपको LiveData की कई विशेषताओं के बारे में जानकारी मिलेगी:

  • LiveData को ऑब्ज़र्व किया जा सकता है. इसका मतलब है कि LiveData ऑब्जेक्ट में मौजूद डेटा में बदलाव होने पर, ऑब्ज़र्वर को सूचना मिलती है.
  • LiveData में डेटा होता है; LiveData एक रैपर है, जिसका इस्तेमाल किसी भी डेटा के साथ किया जा सकता है
  • LiveData को लाइफ़साइकल की जानकारी होती है. इसका मतलब है कि यह सिर्फ़ उन ऑब्ज़र्वर को अपडेट करता है जो लाइफ़साइकल की चालू स्थिति में हैं. जैसे, STARTED या RESUMED.

इस टास्क में, आपको किसी भी डेटा टाइप को LiveData ऑब्जेक्ट में रैप करने का तरीका बताया गया है. इसके लिए, GameViewModel में मौजूद मौजूदा स्कोर और मौजूदा शब्द के डेटा को LiveData में बदलें. बाद के टास्क में, इन LiveData ऑब्जेक्ट में एक ऑब्ज़र्वर जोड़ा जाता है. साथ ही, LiveData को ऑब्ज़र्व करने का तरीका बताया जाता है.

पहला चरण: LiveData का इस्तेमाल करने के लिए, स्कोर और शब्द में बदलाव करना

  1. screens/game पैकेज में जाकर, GameViewModel फ़ाइल खोलें.
  2. score और word वैरिएबल के टाइप को MutableLiveData में बदलें.

    MutableLiveData एक LiveData है, जिसकी वैल्यू बदली जा सकती है. MutableLiveData एक सामान्य क्लास है. इसलिए, आपको यह बताना होगा कि इसमें किस तरह का डेटा मौजूद है.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()
  1. GameViewModel में, init ब्लॉक के अंदर, score और word को शुरू करें. LiveData वैरिएबल की वैल्यू बदलने के लिए, वैरिएबल पर setValue() तरीके का इस्तेमाल किया जाता है. Kotlin में, setValue() प्रॉपर्टी का इस्तेमाल करके value को कॉल किया जा सकता है.
init {

   word.value = ""
   score.value = 0
  ...
}

दूसरा चरण: LiveData ऑब्जेक्ट के रेफ़रंस को अपडेट करना

score और word वैरिएबल अब LiveData टाइप के हैं. इस चरण में, value प्रॉपर्टी का इस्तेमाल करके, इन वैरिएबल के रेफ़रंस बदले जाते हैं.

  1. GameViewModel में, onSkip() तरीके का इस्तेमाल करके, score को score.value में बदलें. ध्यान दें कि score के null होने की वजह से गड़बड़ी हुई है. इस गड़बड़ी को ठीक करें.
  2. गड़बड़ी को ठीक करने के लिए, onSkip() में score.value के बगल में null का निशान लगाएं. इसके बाद, score पर minus() फ़ंक्शन को कॉल करें. यह फ़ंक्शन, null-सुरक्षा के साथ घटाव करता है.
fun onSkip() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.minus(1)
   }
   nextWord()
}
  1. onCorrect() तरीके को इसी तरह अपडेट करें: score वैरिएबल में null की जांच जोड़ें और plus() फ़ंक्शन का इस्तेमाल करें.
fun onCorrect() {
   if (!wordList.isEmpty()) {
       score.value = (score.value)?.plus(1)
   }
   nextWord()
}
  1. GameViewModel में, nextWord() तरीके के अंदर, word रेफ़रंस को word.value में बदलें.
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       word.value = wordList.removeAt(0)
   }
}
  1. GameFragment में, updateWordText() तरीके के अंदर, viewModel.word के रेफ़रंस को viewModel.word.value. में बदलें
/** Methods for updating the UI **/
private fun updateWordText() {
   binding.wordText.text = viewModel.word.value
}
  1. GameFragment में, updateScoreText() तरीके के अंदर, viewModel.score के रेफ़रंस को viewModel.score.value. में बदलें
private fun updateScoreText() {
   binding.scoreText.text = viewModel.score.value.toString()
}
  1. GameFragment में, gameFinished() तरीके के अंदर, viewModel.score के रेफ़रंस को viewModel.score.value में बदलें. ज़रूरी null-सुरक्षा जांच जोड़ें.
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   NavHostFragment.findNavController(this).navigate(action)
}
  1. पक्का करें कि आपके कोड में कोई गड़बड़ी न हो. अपने ऐप्लिकेशन को कंपाइल करें और चलाएं. ऐप्लिकेशन की सुविधाएं पहले की तरह ही काम करनी चाहिए.

यह टास्क, पिछले टास्क से काफ़ी मिलता-जुलता है. इसमें आपने स्कोर और शब्द के डेटा को LiveData ऑब्जेक्ट में बदला था. इस टास्क में, आपको Observer ऑब्जेक्ट को उन LiveData ऑब्जेक्ट से अटैच करना होता है.

  1. onCreateView() तरीके में मौजूद GameFragment, में, मौजूदा स्कोर viewModel.score के लिए LiveData ऑब्जेक्ट से Observer ऑब्जेक्ट अटैच करें. observe() तरीके का इस्तेमाल करें और viewModel के शुरू होने के बाद कोड डालें. कोड को आसान बनाने के लिए, लैम्डा एक्सप्रेशन का इस्तेमाल करें. (लैम्डा एक्सप्रेशन एक ऐसा फ़ंक्शन होता है जिसकी पहचान छिपी होती है. इसे न तो घोषित किया जाता है और न ही इसे तुरंत एक्सप्रेशन के तौर पर पास किया जाता है.)
viewModel.score.observe(this, Observer { newScore ->
})

Observer के रेफ़रंस की समस्या हल करें. इसके लिए, Observer पर क्लिक करें, Alt+Enter (Mac पर Option+Enter) दबाएं, और androidx.lifecycle.Observer इंपोर्ट करें.

  1. आपने अभी-अभी जिस ऑब्ज़र्वर को बनाया है उसे तब एक इवेंट मिलता है, जब ऑब्ज़र्व किए गए LiveData ऑब्जेक्ट में मौजूद डेटा बदलता है. ऑब्ज़र्वर में, स्कोर TextView को नए स्कोर से अपडेट करें.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})
  1. Observer ऑब्जेक्ट को मौजूदा शब्द LiveData ऑब्जेक्ट से अटैच करें. उसी तरीके से करें जिस तरीके से आपने मौजूदा स्कोर में Observer ऑब्जेक्ट अटैच किया था.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
   binding.wordText.text = newWord
})

score या word की वैल्यू बदलने पर, स्क्रीन पर दिखने वाला score या word अब अपने-आप अपडेट हो जाता है.

  1. GameFragment में, updateWordText() और updateScoreText() तरीकों को मिटाएं. साथ ही, उनसे जुड़े सभी रेफ़रंस मिटाएं. अब आपको इनकी ज़रूरत नहीं है, क्योंकि टेक्स्ट व्यू को LiveData ऑब्ज़र्वर मेथड से अपडेट किया जाता है.
  2. अपना ऐप्लिकेशन चलाएं. आपका गेम ऐप्लिकेशन पहले की तरह ही काम करेगा. हालांकि, अब यह LiveData और LiveData ऑब्ज़र्वर का इस्तेमाल करता है.

इनकैप्सुलेशन, किसी ऑब्जेक्ट के कुछ फ़ील्ड को सीधे तौर पर ऐक्सेस करने से रोकने का एक तरीका है. किसी ऑब्जेक्ट को इनकैप्सुलेट करने पर, सार्वजनिक तरीकों का एक सेट दिखता है. ये तरीके, निजी इंटरनल फ़ील्ड में बदलाव करते हैं. एनकैप्सुलेशन का इस्तेमाल करके, यह कंट्रोल किया जा सकता है कि अन्य क्लास इन इंटरनल फ़ील्ड में कैसे बदलाव करें.

आपके मौजूदा कोड में, कोई भी बाहरी क्लास value प्रॉपर्टी का इस्तेमाल करके, score और word वैरिएबल में बदलाव कर सकती है. उदाहरण के लिए, viewModel.score.value का इस्तेमाल करके. इस कोडलैब में डेवलप किए जा रहे ऐप्लिकेशन में, इससे कोई फ़र्क़ नहीं पड़ता. हालांकि, प्रोडक्शन ऐप्लिकेशन में आपको ViewModel ऑब्जेक्ट में मौजूद डेटा को कंट्रोल करना होता है.

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

MutableLiveData बनाम LiveData:

  • नाम से पता चलता है कि MutableLiveData ऑब्जेक्ट में मौजूद डेटा को बदला जा सकता है. ViewModel में मौजूद डेटा में बदलाव किया जा सकता है. इसलिए, यह MutableLiveData का इस्तेमाल करता है.
  • LiveData ऑब्जेक्ट में मौजूद डेटा को पढ़ा जा सकता है, लेकिन बदला नहीं जा सकता. ViewModel के बाहर, डेटा को पढ़ा जा सकता है, लेकिन उसमें बदलाव नहीं किया जा सकता. इसलिए, डेटा को LiveData के तौर पर दिखाया जाना चाहिए.

इस रणनीति को लागू करने के लिए, Kotlin बैकिंग प्रॉपर्टी का इस्तेमाल किया जाता है. बैकिंग प्रॉपर्टी की मदद से, किसी गेटर से ऑब्जेक्ट के अलावा कोई और वैल्यू भी दिखाई जा सकती है. इस टास्क में, आपको GuessTheWord ऐप्लिकेशन में score और word ऑब्जेक्ट के लिए बैकिंग प्रॉपर्टी लागू करनी है.

स्कोर और शब्द में बैकिंग प्रॉपर्टी जोड़ना

  1. GameViewModel में, मौजूदा score ऑब्जेक्ट को private के तौर पर सेट करें.
  2. बैकअप लेने वाली प्रॉपर्टी में इस्तेमाल किए गए नाम रखने के कन्वेंशन का पालन करने के लिए, score को _score में बदलें. _score प्रॉपर्टी, अब गेम के स्कोर का ऐसा वर्शन है जिसमें बदलाव किया जा सकता है. इसका इस्तेमाल सिर्फ़ इंटरनल तौर पर किया जा सकता है.
  3. LiveData टाइप का एक सार्वजनिक वर्शन बनाएं, जिसे score कहा जाता है.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
  1. आपको शुरू करने के दौरान गड़बड़ी का मैसेज दिखता है. यह गड़बड़ी इसलिए होती है, क्योंकि GameFragment के अंदर, score एक LiveData रेफ़रंस है. साथ ही, score अब अपने सेटर को ऐक्सेस नहीं कर सकता. Kotlin में गेटर और सेटर के बारे में ज़्यादा जानने के लिए, गेटर और सेटर देखें.

    गड़बड़ी ठीक करने के लिए, GameViewModel में मौजूद score ऑब्जेक्ट के लिए get() तरीके को बदलें और बैकिंग प्रॉपर्टी _score को वापस लाएं.
val score: LiveData<Int>
   get() = _score
  1. GameViewModel में, score के रेफ़रंस को बदलकर, उसके इंटरनल म्यूटबल वर्शन _score पर सेट करें.
init {
   ...
   _score.value = 0
   ...
}

...
fun onSkip() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.minus(1)
   }
  ...
}

fun onCorrect() {
   if (!wordList.isEmpty()) {
       _score.value = (score.value)?.plus(1)
   }
   ...
}
  1. word ऑब्जेक्ट का नाम बदलकर _word करें. साथ ही, इसके लिए बैकिंग प्रॉपर्टी जोड़ें. ऐसा ही आपने score ऑब्जेक्ट के लिए किया था.
// The current word
private val _word = MutableLiveData<String>()
val word: LiveData<String>
   get() = _word
...
init {
   _word.value = ""
   ...
}
...
private fun nextWord() {
   if (!wordList.isEmpty()) {
       //Select and remove a word from the list
       _word.value = wordList.removeAt(0)
   }
}

बहुत बढ़िया, आपने LiveData ऑब्जेक्ट word और score को शामिल कर लिया है.

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

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

ऑब्ज़र्वर पैटर्न

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

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

पहला चरण: गेम खत्म होने वाले इवेंट का पता लगाने के लिए LiveData का इस्तेमाल करना

इस टास्क में, गेम खत्म होने वाले इवेंट को मॉडल बनाने के लिए, LiveData ऑब्ज़र्वर पैटर्न का इस्तेमाल किया जाता है.

  1. GameViewModel में, Boolean MutableLiveData ऑब्जेक्ट बनाएं. इसका नाम _eventGameFinish रखें. इस ऑब्जेक्ट में, गेम खत्म होने का इवेंट शामिल होगा.
  2. _eventGameFinish ऑब्जेक्ट को शुरू करने के बाद, eventGameFinish नाम की बैकिंग प्रॉपर्टी बनाएं और उसे शुरू करें.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
   get() = _eventGameFinish
  1. GameViewModel में, onGameFinish() का तरीका जोड़ें. इस तरीके में, गेम खत्म होने वाले इवेंट eventGameFinish को true पर सेट करें.
/** Method for the game completed event **/
fun onGameFinish() {
   _eventGameFinish.value = true
}
  1. GameViewModel में, nextWord() तरीके के अंदर, अगर शब्दों की सूची खाली है, तो गेम खत्म करें.
private fun nextWord() {
   if (wordList.isEmpty()) {
       onGameFinish()
   } else {
       //Select and remove a _word from the list
       _word.value = wordList.removeAt(0)
   }
}
  1. GameFragment में, onCreateView() के अंदर, viewModel को शुरू करने के बाद, eventGameFinish से एक ऑब्ज़र्वर अटैच करें. observe() तरीके का इस्तेमाल करें. लैंबडा फ़ंक्शन के अंदर, gameFinished() तरीके को कॉल करें.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
   if (hasFinished) gameFinished()
})
  1. ऐप्लिकेशन चलाएं, गेम खेलें, और सभी शब्दों को देखें. ऐप्लिकेशन, गेम फ़्रैगमेंट में तब तक नहीं रहता, जब तक गेम खत्म करें पर टैप नहीं किया जाता. इसके बजाय, यह अपने-आप स्कोर स्क्रीन पर चला जाता है.

    शब्दों की सूची खाली होने के बाद, eventGameFinish सेट हो जाता है. इसके बाद, गेम फ़्रैगमेंट में ऑब्ज़र्वर से जुड़ी विधि को कॉल किया जाता है और ऐप्लिकेशन, स्क्रीन फ़्रैगमेंट पर नेविगेट करता है.
  2. आपने जो कोड जोड़ा है उसकी वजह से, लाइफ़साइकल से जुड़ी समस्या आ गई है. समस्या को समझने के लिए, GameFragment क्लास में, gameFinished() तरीके में नेविगेशन कोड पर टिप्पणी करें. पक्का करें कि आपने तरीके में Toast मैसेज को शामिल किया हो.
private fun gameFinished() {
       Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
//        val action = GameFragmentDirections.actionGameToScore()
//        action.score = viewModel.score.value?:0
//        NavHostFragment.findNavController(this).navigate(action)
   }
  1. ऐप्लिकेशन चलाएं, गेम खेलें, और सभी शब्दों को देखें. गेम की स्क्रीन पर सबसे नीचे, "गेम अभी-अभी खत्म हुआ है" मैसेज वाला एक सूचना मैसेज कुछ समय के लिए दिखता है. यह उम्मीद के मुताबिक है.

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

दूसरा चरण: गेम खत्म होने वाले इवेंट को रीसेट करना

आम तौर पर, LiveData सिर्फ़ डेटा में बदलाव होने पर, ऑब्ज़र्वर को अपडेट भेजता है. हालांकि, इसका एक अपवाद यह है कि जब ऑब्ज़र्वर की स्थिति 'बंद है' से बदलकर 'चालू है' हो जाती है, तब भी ऑब्ज़र्वर को अपडेट मिलते हैं.

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

इस टास्क में, आपको इस समस्या को ठीक करना है. साथ ही, GameViewModel में eventGameFinish फ़्लैग को रीसेट करके, सूचना को सिर्फ़ एक बार दिखाना है.

  1. GameViewModel में, गेम खत्म होने वाले इवेंट को रीसेट करने के लिए, onGameFinishComplete() तरीका जोड़ें, _eventGameFinish.
/** Method for the game completed event **/

fun onGameFinishComplete() {
   _eventGameFinish.value = false
}
  1. GameFragment में, gameFinished() के आखिर में, viewModel ऑब्जेक्ट पर onGameFinishComplete() को कॉल करें. (फ़िलहाल, नेविगेशन कोड को gameFinished() के तौर पर मार्क करें.)
private fun gameFinished() {
   ...
   viewModel.onGameFinishComplete()
}
  1. ऐप्लिकेशन चलाएं और गेम खेलें. सभी शब्दों को पढ़ लें. इसके बाद, डिवाइस का स्क्रीन ओरिएंटेशन बदलें. यह सूचना सिर्फ़ एक बार दिखती है.
  2. GameFragment में, gameFinished() तरीके के अंदर, नेविगेशन कोड से टिप्पणी हटाएं.

    Android Studio में टिप्पणी हटाने के लिए, उन लाइनों को चुनें जिन पर टिप्पणी की गई है. इसके बाद, Control+/ (Mac पर Command+/) दबाएं.
private fun gameFinished() {
   Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
   val action = GameFragmentDirections.actionGameToScore()
   action.score = viewModel.score.value?:0
   findNavController(this).navigate(action)
   viewModel.onGameFinishComplete()
}

अगर Android Studio आपसे ऐसा करने के लिए कहता है, तो androidx.navigation.fragment.NavHostFragment.findNavController इंपोर्ट करें.

  1. ऐप्लिकेशन चलाएं और गेम खेलें. पक्का करें कि सभी शब्दों को पढ़ने के बाद, ऐप्लिकेशन अपने-आप फ़ाइनल स्कोर वाली स्क्रीन पर चला जाए.

बहुत बढ़िया! आपका ऐप्लिकेशन, गेम खत्म होने वाले इवेंट को ट्रिगर करने के लिए LiveData का इस्तेमाल करता है. इससे LiveData, गेम फ़्रैगमेंट को यह सूचना देता है कि शब्दों की सूची खाली है.GameViewModel इसके बाद, गेम फ़्रैगमेंट, स्कोर फ़्रैगमेंट पर नेविगेट करता है.

इस टास्क में, आपको ScoreViewModel में मौजूद LiveData ऑब्जेक्ट के स्कोर में बदलाव करना है. साथ ही, इसमें एक ऑब्ज़र्वर अटैच करना है. यह टास्क, GameViewModel में LiveData जोड़ने के टास्क जैसा ही है.

आप ScoreViewModel में ये बदलाव, पूरी जानकारी देने के लिए करते हैं, ताकि आपके ऐप्लिकेशन में मौजूद सभी डेटा में LiveData का इस्तेमाल हो.

  1. ScoreViewModel में, score वैरिएबल टाइप को MutableLiveData में बदलें. इसे _score के तौर पर नाम बदलें और बैकिंग प्रॉपर्टी जोड़ें.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
   get() = _score
  1. ScoreViewModel में, init ब्लॉक के अंदर, _score को शुरू करें. आपके पास init ब्लॉक में मौजूद लॉग को हटाने या छोड़ने का विकल्प होता है.
init {
   _score.value = finalScore
}
  1. ScoreFragment में, onCreateView() के अंदर, viewModel को शुरू करने के बाद, स्कोर LiveData ऑब्जेक्ट के लिए एक ऑब्ज़र्वर अटैच करें. लैंबडा एक्सप्रेशन में, स्कोर टेक्स्ट व्यू के लिए स्कोर की वैल्यू सेट करें. ViewModel से उस कोड को हटाएं जो स्कोर वैल्यू के साथ टेक्स्ट व्यू को सीधे तौर पर असाइन करता है.

जोड़ने के लिए कोड:

// Add observer for score
viewModel.score.observe(this, Observer { newScore ->
   binding.scoreText.text = newScore.toString()
})

हटाने के लिए कोड:

binding.scoreText.text = viewModel.score.toString()

Android Studio से कहा जाने पर, androidx.lifecycle.Observer इंपोर्ट करें.

  1. अपना ऐप्लिकेशन चलाएं और गेम खेलें. ऐप्लिकेशन पहले की तरह ही काम करेगा. हालांकि, अब यह स्कोर को अपडेट करने के लिए LiveData और ऑब्ज़र्वर का इस्तेमाल करता है.

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

ऐप्लिकेशन के स्टार्टर कोड में फिर से खेलें बटन शामिल है, लेकिन यह बटन छिपा हुआ है.

  1. res/layout/score_fragment.xml में, play_again_button बटन के लिए, visibility एट्रिब्यूट की वैल्यू को visible पर सेट करें.
<Button
   android:id="@+id/play_again_button"
...
   android:visibility="visible"
 />
  1. ScoreViewModel में, LiveData ऑब्जेक्ट जोड़ें, ताकि _eventPlayAgain नाम का Boolean होल्ड किया जा सके. इस ऑब्जेक्ट का इस्तेमाल, स्कोर स्क्रीन से गेम स्क्रीन पर जाने के लिए LiveData इवेंट को सेव करने के लिए किया जाता है.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
   get() = _eventPlayAgain
  1. ScoreViewModel में, इवेंट को सेट और रीसेट करने के तरीके तय करें, _eventPlayAgain.
fun onPlayAgain() {
   _eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
   _eventPlayAgain.value = false
}
  1. ScoreFragment में, eventPlayAgain के लिए ऑब्ज़र्वर जोड़ें. कोड को onCreateView() के आखिर में, return स्टेटमेंट से पहले रखें. लैम्डा एक्सप्रेशन में, गेम स्क्रीन पर वापस जाएं और eventPlayAgain को रीसेट करें.
// Navigates back to game when button is pressed
viewModel.eventPlayAgain.observe(this, Observer { playAgain ->
   if (playAgain) {
      findNavController().navigate(ScoreFragmentDirections.actionRestart())
       viewModel.onPlayAgainComplete()
   }
})

Android Studio से पूछे जाने पर, androidx.navigation.fragment.findNavController इंपोर्ट करें.

  1. ScoreFragment में, onCreateView() के अंदर, PlayAgain बटन में क्लिक लिसनर जोड़ें और viewModel.onPlayAgain() को कॉल करें.
binding.playAgainButton.setOnClickListener {  viewModel.onPlayAgain()  }
  1. अपना ऐप्लिकेशन चलाएं और गेम खेलें. गेम खत्म होने पर, स्कोर स्क्रीन पर फ़ाइनल स्कोर और फिर से खेलें बटन दिखता है. PlayAgain बटन पर टैप करें. इसके बाद, ऐप्लिकेशन आपको गेम की स्क्रीन पर ले जाएगा, ताकि आप गेम को फिर से खेल सकें.

बहुत बढ़िया! आपने अपने ऐप्लिकेशन के आर्किटेक्चर में बदलाव किया है, ताकि ViewModel में LiveData ऑब्जेक्ट का इस्तेमाल किया जा सके. साथ ही, आपने LiveData ऑब्जेक्ट में ऑब्ज़र्वर अटैच किए हैं. LiveData, ऑब्ज़र्वर ऑब्जेक्ट को तब सूचना देता है, जब LiveData की वैल्यू बदल जाती है.

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

LiveData

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

LiveData जोड़ने के लिए

  • ViewModel में मौजूद डेटा वैरिएबल के टाइप को LiveData या MutableLiveData में बदलें.

MutableLiveData एक LiveData ऑब्जेक्ट है, जिसकी वैल्यू बदली जा सकती है. MutableLiveData एक सामान्य क्लास है. इसलिए, आपको यह बताना होगा कि इसमें किस तरह का डेटा मौजूद है.

  • LiveData वैरिएबल में मौजूद डेटा की वैल्यू बदलने के लिए, LiveData वैरिएबल पर setValue() तरीके का इस्तेमाल करें.

LiveData को इनकैप्सुलेट करने के लिए

  • ViewModel में मौजूद LiveData में बदलाव किया जा सकता हो. ViewModel के बाहर, LiveData को पढ़ा जा सकता हो. इसे Kotlin की बैकिंग प्रॉपर्टी का इस्तेमाल करके लागू किया जा सकता है.
  • Kotlin की बैकिंग प्रॉपर्टी की मदद से, गेटर से ऑब्जेक्ट के अलावा कुछ और भी वापस लाया जा सकता है.
  • LiveData को इनकैप्सुलेट करने के लिए, ViewModel के अंदर private MutableLiveData का इस्तेमाल करें. साथ ही, ViewModel के बाहर LiveData बैकिंग प्रॉपर्टी दिखाएं.

ऑब्ज़र्वेबल LiveData

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

Udacity का कोर्स:

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

अन्य:

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

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

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

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

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

पहला सवाल

ViewModel में सेव किए गए LiveData को कैसे इनकैप्सुलेट किया जाता है, ताकि बाहरी ऑब्जेक्ट डेटा को अपडेट किए बिना पढ़ सकें?

  • ViewModel ऑब्जेक्ट में, डेटा के टाइप को private LiveData में बदलें. MutableLiveData टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.
  • ViewModel ऑब्जेक्ट में, डेटा के टाइप को private MutableLiveData में बदलें. LiveData टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.
  • यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में, डेटा के टाइप को private MutableLiveData में बदलें. LiveData टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.
  • ViewModel ऑब्जेक्ट में, डेटा के डेटा टाइप को LiveData में बदलें. LiveData टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.

दूसरा सवाल

LiveData, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर (जैसे कि फ़्रैगमेंट) को कब अपडेट करता है?

  • फिर से शुरू की गई
  • बैकग्राउंड में
  • रोकी गई
  • बंद किया गया

तीसरा सवाल

LiveData ऑब्ज़र्वर पैटर्न में, ऑब्ज़र्व किया जा सकने वाला आइटम क्या है (क्या ऑब्ज़र्व किया जाता है)?

  • ऑब्ज़र्वर मेथड
  • LiveData ऑब्जेक्ट में मौजूद डेटा
  • यूज़र इंटरफ़ेस (यूआई) कंट्रोलर
  • ViewModel ऑब्जेक्ट

अगला सबक शुरू करें: 5.3: ViewModel और LiveData के साथ डेटा बाइंडिंग

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