Android Kotlin से जुड़े बुनियादी बातें 05.2: LiveData और LiveData ऑब्ज़र्वर

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

परिचय

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

गेम स्क्रीन

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

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

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

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

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

इस टास्क में, किसी भी तरह के डेटा को LiveData ऑब्जेक्ट में रैप करने का तरीका जानें. इसके लिए, मौजूदा स्कोर और मौजूदा शब्द के डेटा को GameViewModel में बदलें. इसके लिए, चुनें. बाद के टास्क में, 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 में, आप value प्रॉपर्टी का इस्तेमाल करके setValue() को कॉल कर सकते हैं.
init {

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

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

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

  1. GameViewModel के onSkip() तरीके में, score को score.value में बदलें. score के शायद null होने की गड़बड़ी देखें. इसके बाद, आप इस गड़बड़ी को ठीक करते हैं.
  2. गड़बड़ी को ठीक करने के लिए, onSkip() में null चेक को score.value में जोड़ें. फिर score पर minus() फ़ंक्शन को कॉल करें, जो null-safety के साथ घटाता है.
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. GameFragment, में onCreateView() तरीके से, मौजूदा स्कोर viewModel.score के लिए, LiveData ऑब्जेक्ट में एक Observer ऑब्जेक्ट अटैच करें. observe() तरीके का इस्तेमाल करें और viewModel के शुरू होने के बाद कोड डालें. कोड को आसान बनाने के लिए Lambda के एक्सप्रेशन का इस्तेमाल करें. (lambda एक्सप्रेशन एक ऐसा फ़ंक्शन है जिसकी पहचान ज़ाहिर नहीं की जाती और जिसे #33 नहीं बताया जाता, लेकिन इसे एक्सप्रेशन के तौर पर तुरंत पास किया जाता है.
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 ऑब्ज़र्वर का इस्तेमाल करता है.

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

आपके मौजूदा कोड में, कोई भी बाहरी क्लास, 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 में गैटर और सेटर के बारे में ज़्यादा जानने के लिए, Getter and Setters को देखें.

    इस गड़बड़ी को ठीक करने के लिए, 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 का डेटा बदलता है, तो उसकी स्थिति में बदलाव होता है. ViewModel से फ़्रैगमेंट तक बात करने के लिए, LiveData क्लास ज़रूरी हैं.

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

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

  1. GameViewModel में, _eventGameFinish नाम का एक Boolean MutableLiveData ऑब्जेक्ट बनाएं. इस ऑब्जेक्ट में गेम खत्म होने वाला इवेंट होगा.
  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() वाले तरीके का इस्तेमाल करें. Lambda के फ़ंक्शन के अंदर, 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 में, गेम खत्म होने के इवेंट _eventGameFinish को रीसेट करने के लिए, onGameFinishComplete() का तरीका जोड़ें.
/** 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 का इस्तेमाल करके, 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 ऑब्जेक्ट के लिए ऑब्ज़र्वर अटैच करें. Lambda के एक्सप्रेशन में, स्कोर की वैल्यू को स्कोर के टेक्स्ट व्यू पर सेट करें. वह कोड हटाएं जो 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 के लिए ऑब्ज़र्वर जोड़ें. कोड को return स्टेटमेंट के पहले, onCreateView() के आखिर में रखें. Lambda के एक्सप्रेशन में, गेम स्क्रीन पर वापस जाएं और 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 से यूज़र इंटरफ़ेस (यूआई) कंट्रोलर से संपर्क करने के लिए किया जा सकता है.

Udcity कोर्स:

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

अन्य:

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

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

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

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

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

पहला सवाल

आप 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 की बुनियादी बातें कोडलैब का लैंडिंग पेज देखें.