यह कोडलैब, 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 ऐप्लिकेशन को स्टार्टर कोड के तौर पर इस्तेमाल किया जा सकता है. इसके अलावा, स्टार्टर ऐप्लिकेशन भी डाउनलोड किया जा सकता है.
- (ज़रूरी नहीं) अगर आपको पिछले कोडलैब का कोड इस्तेमाल नहीं करना है, तो इस कोडलैब के लिए स्टार्टर कोड डाउनलोड करें. कोड को अनज़िप करें और Android Studio में प्रोजेक्ट खोलें.
- ऐप्लिकेशन चलाएं और गेम खेलें.
- ध्यान दें कि छोड़ें बटन पर क्लिक करने से, अगला शब्द दिखता है और स्कोर में एक अंक की कमी आती है. वहीं, ठीक है बटन पर क्लिक करने से, अगला शब्द दिखता है और स्कोर में एक अंक की बढ़ोतरी होती है. गेम खत्म करें बटन से गेम खत्म हो जाता है.
LiveData, ऑब्ज़र्व की जा सकने वाली डेटा होल्डर क्लास है. यह लाइफ़साइकल के बारे में जानती है. उदाहरण के लिए, GuessTheWord ऐप्लिकेशन में मौजूदा स्कोर के चारों ओर LiveData रैप किया जा सकता है. इस कोडलैब में, आपको LiveData की कई विशेषताओं के बारे में जानकारी मिलेगी:
LiveDataको ऑब्ज़र्व किया जा सकता है. इसका मतलब है किLiveDataऑब्जेक्ट में मौजूद डेटा में बदलाव होने पर, ऑब्ज़र्वर को सूचना मिलती है.LiveDataमें डेटा होता है;LiveDataएक रैपर है, जिसका इस्तेमाल किसी भी डेटा के साथ किया जा सकता हैLiveDataको लाइफ़साइकल की जानकारी होती है. इसका मतलब है कि यह सिर्फ़ उन ऑब्ज़र्वर को अपडेट करता है जो लाइफ़साइकल की चालू स्थिति में हैं. जैसे,STARTEDयाRESUMED.
इस टास्क में, आपको किसी भी डेटा टाइप को LiveData ऑब्जेक्ट में रैप करने का तरीका बताया गया है. इसके लिए, GameViewModel में मौजूद मौजूदा स्कोर और मौजूदा शब्द के डेटा को LiveData में बदलें. बाद के टास्क में, इन LiveData ऑब्जेक्ट में एक ऑब्ज़र्वर जोड़ा जाता है. साथ ही, LiveData को ऑब्ज़र्व करने का तरीका बताया जाता है.
पहला चरण: LiveData का इस्तेमाल करने के लिए, स्कोर और शब्द में बदलाव करना
screens/gameपैकेज में जाकर,GameViewModelफ़ाइल खोलें.scoreऔरwordवैरिएबल के टाइप कोMutableLiveDataमें बदलें.MutableLiveDataएकLiveDataहै, जिसकी वैल्यू बदली जा सकती है.MutableLiveDataएक सामान्य क्लास है. इसलिए, आपको यह बताना होगा कि इसमें किस तरह का डेटा मौजूद है.
// The current word
val word = MutableLiveData<String>()
// The current score
val score = MutableLiveData<Int>()GameViewModelमें,initब्लॉक के अंदर,scoreऔरwordको शुरू करें.LiveDataवैरिएबल की वैल्यू बदलने के लिए, वैरिएबल परsetValue()तरीके का इस्तेमाल किया जाता है. Kotlin में,setValue()प्रॉपर्टी का इस्तेमाल करकेvalueको कॉल किया जा सकता है.
init {
word.value = ""
score.value = 0
...
}दूसरा चरण: LiveData ऑब्जेक्ट के रेफ़रंस को अपडेट करना
score और word वैरिएबल अब LiveData टाइप के हैं. इस चरण में, value प्रॉपर्टी का इस्तेमाल करके, इन वैरिएबल के रेफ़रंस बदले जाते हैं.
GameViewModelमें,onSkip()तरीके का इस्तेमाल करके,scoreकोscore.valueमें बदलें. ध्यान दें किscoreकेnullहोने की वजह से गड़बड़ी हुई है. इस गड़बड़ी को ठीक करें.- गड़बड़ी को ठीक करने के लिए,
onSkip()मेंscore.valueके बगल मेंnullका निशान लगाएं. इसके बाद,scoreपरminus()फ़ंक्शन को कॉल करें. यह फ़ंक्शन,null-सुरक्षा के साथ घटाव करता है.
fun onSkip() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.minus(1)
}
nextWord()
}onCorrect()तरीके को इसी तरह अपडेट करें:scoreवैरिएबल मेंnullकी जांच जोड़ें औरplus()फ़ंक्शन का इस्तेमाल करें.
fun onCorrect() {
if (!wordList.isEmpty()) {
score.value = (score.value)?.plus(1)
}
nextWord()
}GameViewModelमें,nextWord()तरीके के अंदर,wordरेफ़रंस कोword.valueमें बदलें.
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word.value = wordList.removeAt(0)
}
}GameFragmentमें,updateWordText()तरीके के अंदर,viewModel.wordके रेफ़रंस कोviewModel.word.value.में बदलें
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = viewModel.word.value
}GameFragmentमें,updateScoreText()तरीके के अंदर,viewModel.scoreके रेफ़रंस कोviewModel.score.value.में बदलें
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.value.toString()
}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)
}- पक्का करें कि आपके कोड में कोई गड़बड़ी न हो. अपने ऐप्लिकेशन को कंपाइल करें और चलाएं. ऐप्लिकेशन की सुविधाएं पहले की तरह ही काम करनी चाहिए.
यह टास्क, पिछले टास्क से काफ़ी मिलता-जुलता है. इसमें आपने स्कोर और शब्द के डेटा को LiveData ऑब्जेक्ट में बदला था. इस टास्क में, आपको Observer ऑब्जेक्ट को उन LiveData ऑब्जेक्ट से अटैच करना होता है.
onCreateView()तरीके में मौजूदGameFragment,में, मौजूदा स्कोरviewModel.scoreके लिएLiveDataऑब्जेक्ट सेObserverऑब्जेक्ट अटैच करें.observe()तरीके का इस्तेमाल करें औरviewModelके शुरू होने के बाद कोड डालें. कोड को आसान बनाने के लिए, लैम्डा एक्सप्रेशन का इस्तेमाल करें. (लैम्डा एक्सप्रेशन एक ऐसा फ़ंक्शन होता है जिसकी पहचान छिपी होती है. इसे न तो घोषित किया जाता है और न ही इसे तुरंत एक्सप्रेशन के तौर पर पास किया जाता है.)
viewModel.score.observe(this, Observer { newScore ->
})Observer के रेफ़रंस की समस्या हल करें. इसके लिए, Observer पर क्लिक करें, Alt+Enter (Mac पर Option+Enter) दबाएं, और androidx.lifecycle.Observer इंपोर्ट करें.
- आपने अभी-अभी जिस ऑब्ज़र्वर को बनाया है उसे तब एक इवेंट मिलता है, जब ऑब्ज़र्व किए गए
LiveDataऑब्जेक्ट में मौजूद डेटा बदलता है. ऑब्ज़र्वर में, स्कोरTextViewको नए स्कोर से अपडेट करें.
/** Setting up LiveData observation relationship **/
viewModel.score.observe(this, Observer { newScore ->
binding.scoreText.text = newScore.toString()
})Observerऑब्जेक्ट को मौजूदा शब्दLiveDataऑब्जेक्ट से अटैच करें. उसी तरीके से करें जिस तरीके से आपने मौजूदा स्कोर मेंObserverऑब्जेक्ट अटैच किया था.
/** Setting up LiveData observation relationship **/
viewModel.word.observe(this, Observer { newWord ->
binding.wordText.text = newWord
})score या word की वैल्यू बदलने पर, स्क्रीन पर दिखने वाला score या word अब अपने-आप अपडेट हो जाता है.
GameFragmentमें,updateWordText()औरupdateScoreText()तरीकों को मिटाएं. साथ ही, उनसे जुड़े सभी रेफ़रंस मिटाएं. अब आपको इनकी ज़रूरत नहीं है, क्योंकि टेक्स्ट व्यू कोLiveDataऑब्ज़र्वर मेथड से अपडेट किया जाता है.- अपना ऐप्लिकेशन चलाएं. आपका गेम ऐप्लिकेशन पहले की तरह ही काम करेगा. हालांकि, अब यह
LiveDataऔरLiveDataऑब्ज़र्वर का इस्तेमाल करता है.
इनकैप्सुलेशन, किसी ऑब्जेक्ट के कुछ फ़ील्ड को सीधे तौर पर ऐक्सेस करने से रोकने का एक तरीका है. किसी ऑब्जेक्ट को इनकैप्सुलेट करने पर, सार्वजनिक तरीकों का एक सेट दिखता है. ये तरीके, निजी इंटरनल फ़ील्ड में बदलाव करते हैं. एनकैप्सुलेशन का इस्तेमाल करके, यह कंट्रोल किया जा सकता है कि अन्य क्लास इन इंटरनल फ़ील्ड में कैसे बदलाव करें.
आपके मौजूदा कोड में, कोई भी बाहरी क्लास value प्रॉपर्टी का इस्तेमाल करके, score और word वैरिएबल में बदलाव कर सकती है. उदाहरण के लिए, viewModel.score.value का इस्तेमाल करके. इस कोडलैब में डेवलप किए जा रहे ऐप्लिकेशन में, इससे कोई फ़र्क़ नहीं पड़ता. हालांकि, प्रोडक्शन ऐप्लिकेशन में आपको ViewModel ऑब्जेक्ट में मौजूद डेटा को कंट्रोल करना होता है.
आपके ऐप्लिकेशन में मौजूद डेटा में सिर्फ़ ViewModel को बदलाव करने का अधिकार होना चाहिए. हालांकि, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर को डेटा पढ़ने की ज़रूरत होती है. इसलिए, डेटा फ़ील्ड को पूरी तरह से निजी नहीं रखा जा सकता. अपने ऐप्लिकेशन के डेटा को इनकैप्सुलेट करने के लिए, MutableLiveData और LiveData, दोनों ऑब्जेक्ट का इस्तेमाल करें.
MutableLiveData बनाम LiveData:
- नाम से पता चलता है कि
MutableLiveDataऑब्जेक्ट में मौजूद डेटा को बदला जा सकता है.ViewModelमें मौजूद डेटा में बदलाव किया जा सकता है. इसलिए, यहMutableLiveDataका इस्तेमाल करता है. LiveDataऑब्जेक्ट में मौजूद डेटा को पढ़ा जा सकता है, लेकिन बदला नहीं जा सकता.ViewModelके बाहर, डेटा को पढ़ा जा सकता है, लेकिन उसमें बदलाव नहीं किया जा सकता. इसलिए, डेटा कोLiveDataके तौर पर दिखाया जाना चाहिए.
इस रणनीति को लागू करने के लिए, Kotlin बैकिंग प्रॉपर्टी का इस्तेमाल किया जाता है. बैकिंग प्रॉपर्टी की मदद से, किसी गेटर से ऑब्जेक्ट के अलावा कोई और वैल्यू भी दिखाई जा सकती है. इस टास्क में, आपको GuessTheWord ऐप्लिकेशन में score और word ऑब्जेक्ट के लिए बैकिंग प्रॉपर्टी लागू करनी है.
स्कोर और शब्द में बैकिंग प्रॉपर्टी जोड़ना
GameViewModelमें, मौजूदाscoreऑब्जेक्ट कोprivateके तौर पर सेट करें.- बैकअप लेने वाली प्रॉपर्टी में इस्तेमाल किए गए नाम रखने के कन्वेंशन का पालन करने के लिए,
scoreको_scoreमें बदलें._scoreप्रॉपर्टी, अब गेम के स्कोर का ऐसा वर्शन है जिसमें बदलाव किया जा सकता है. इसका इस्तेमाल सिर्फ़ इंटरनल तौर पर किया जा सकता है. LiveDataटाइप का एक सार्वजनिक वर्शन बनाएं, जिसेscoreकहा जाता है.
// The current score
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>- आपको शुरू करने के दौरान गड़बड़ी का मैसेज दिखता है. यह गड़बड़ी इसलिए होती है, क्योंकि
GameFragmentके अंदर,scoreएकLiveDataरेफ़रंस है. साथ ही,scoreअब अपने सेटर को ऐक्सेस नहीं कर सकता. Kotlin में गेटर और सेटर के बारे में ज़्यादा जानने के लिए, गेटर और सेटर देखें.
गड़बड़ी ठीक करने के लिए,GameViewModelमें मौजूदscoreऑब्जेक्ट के लिएget()तरीके को बदलें और बैकिंग प्रॉपर्टी_scoreको वापस लाएं.
val score: LiveData<Int>
get() = _scoreGameViewModelमें,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)
}
...
}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 ऑब्ज़र्वर पैटर्न का इस्तेमाल किया जाता है.
GameViewModelमें,BooleanMutableLiveDataऑब्जेक्ट बनाएं. इसका नाम_eventGameFinishरखें. इस ऑब्जेक्ट में, गेम खत्म होने का इवेंट शामिल होगा._eventGameFinishऑब्जेक्ट को शुरू करने के बाद,eventGameFinishनाम की बैकिंग प्रॉपर्टी बनाएं और उसे शुरू करें.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinishGameViewModelमें,onGameFinish()का तरीका जोड़ें. इस तरीके में, गेम खत्म होने वाले इवेंटeventGameFinishकोtrueपर सेट करें.
/** Method for the game completed event **/
fun onGameFinish() {
_eventGameFinish.value = true
}GameViewModelमें,nextWord()तरीके के अंदर, अगर शब्दों की सूची खाली है, तो गेम खत्म करें.
private fun nextWord() {
if (wordList.isEmpty()) {
onGameFinish()
} else {
//Select and remove a _word from the list
_word.value = wordList.removeAt(0)
}
}GameFragmentमें,onCreateView()के अंदर,viewModelको शुरू करने के बाद,eventGameFinishसे एक ऑब्ज़र्वर अटैच करें.observe()तरीके का इस्तेमाल करें. लैंबडा फ़ंक्शन के अंदर,gameFinished()तरीके को कॉल करें.
// Observer for the Game finished event
viewModel.eventGameFinish.observe(this, Observer<Boolean> { hasFinished ->
if (hasFinished) gameFinished()
})- ऐप्लिकेशन चलाएं, गेम खेलें, और सभी शब्दों को देखें. ऐप्लिकेशन, गेम फ़्रैगमेंट में तब तक नहीं रहता, जब तक गेम खत्म करें पर टैप नहीं किया जाता. इसके बजाय, यह अपने-आप स्कोर स्क्रीन पर चला जाता है.
शब्दों की सूची खाली होने के बाद,eventGameFinishसेट हो जाता है. इसके बाद, गेम फ़्रैगमेंट में ऑब्ज़र्वर से जुड़ी विधि को कॉल किया जाता है और ऐप्लिकेशन, स्क्रीन फ़्रैगमेंट पर नेविगेट करता है. - आपने जो कोड जोड़ा है उसकी वजह से, लाइफ़साइकल से जुड़ी समस्या आ गई है. समस्या को समझने के लिए,
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)
}
- ऐप्लिकेशन चलाएं, गेम खेलें, और सभी शब्दों को देखें. गेम की स्क्रीन पर सबसे नीचे, "गेम अभी-अभी खत्म हुआ है" मैसेज वाला एक सूचना मैसेज कुछ समय के लिए दिखता है. यह उम्मीद के मुताबिक है.
अब डिवाइस या एम्युलेटर को घुमाएं. यह सूचना फिर से दिखती है! डिवाइस को कुछ और बार घुमाएं. ऐसा हो सकता है कि आपको हर बार सूचना दिखे. यह एक बग है, क्योंकि गेम खत्म होने पर सूचना सिर्फ़ एक बार दिखनी चाहिए. फ़्रैगमेंट को फिर से बनाने पर, हर बार सूचना नहीं दिखनी चाहिए. अगले टास्क में इस समस्या को हल किया जा सकता है.
|
|
दूसरा चरण: गेम खत्म होने वाले इवेंट को रीसेट करना
आम तौर पर, LiveData सिर्फ़ डेटा में बदलाव होने पर, ऑब्ज़र्वर को अपडेट भेजता है. हालांकि, इसका एक अपवाद यह है कि जब ऑब्ज़र्वर की स्थिति 'बंद है' से बदलकर 'चालू है' हो जाती है, तब भी ऑब्ज़र्वर को अपडेट मिलते हैं.
इसलिए, आपके ऐप्लिकेशन में गेम खत्म होने का मैसेज बार-बार दिखता है. स्क्रीन रोटेट होने के बाद, गेम फ़्रैगमेंट को फिर से बनाया जाता है. इससे वह निष्क्रिय से सक्रिय स्थिति में आ जाता है. फ़्रैगमेंट में मौजूद ऑब्ज़र्वर, मौजूदा ViewModel से फिर से कनेक्ट हो जाता है और उसे मौजूदा डेटा मिलता है. gameFinished() वाला तरीका फिर से ट्रिगर होता है और सूचना दिखती है.
इस टास्क में, आपको इस समस्या को ठीक करना है. साथ ही, GameViewModel में eventGameFinish फ़्लैग को रीसेट करके, सूचना को सिर्फ़ एक बार दिखाना है.
GameViewModelमें, गेम खत्म होने वाले इवेंट को रीसेट करने के लिए,onGameFinishComplete()तरीका जोड़ें,_eventGameFinish.
/** Method for the game completed event **/
fun onGameFinishComplete() {
_eventGameFinish.value = false
}GameFragmentमें,gameFinished()के आखिर में,viewModelऑब्जेक्ट परonGameFinishComplete()को कॉल करें. (फ़िलहाल, नेविगेशन कोड कोgameFinished()के तौर पर मार्क करें.)
private fun gameFinished() {
...
viewModel.onGameFinishComplete()
}- ऐप्लिकेशन चलाएं और गेम खेलें. सभी शब्दों को पढ़ लें. इसके बाद, डिवाइस का स्क्रीन ओरिएंटेशन बदलें. यह सूचना सिर्फ़ एक बार दिखती है.
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 इंपोर्ट करें.
- ऐप्लिकेशन चलाएं और गेम खेलें. पक्का करें कि सभी शब्दों को पढ़ने के बाद, ऐप्लिकेशन अपने-आप फ़ाइनल स्कोर वाली स्क्रीन पर चला जाए.
|
|
बहुत बढ़िया! आपका ऐप्लिकेशन, गेम खत्म होने वाले इवेंट को ट्रिगर करने के लिए LiveData का इस्तेमाल करता है. इससे LiveData, गेम फ़्रैगमेंट को यह सूचना देता है कि शब्दों की सूची खाली है.GameViewModel इसके बाद, गेम फ़्रैगमेंट, स्कोर फ़्रैगमेंट पर नेविगेट करता है.
इस टास्क में, आपको ScoreViewModel में मौजूद LiveData ऑब्जेक्ट के स्कोर में बदलाव करना है. साथ ही, इसमें एक ऑब्ज़र्वर अटैच करना है. यह टास्क, GameViewModel में LiveData जोड़ने के टास्क जैसा ही है.
आप ScoreViewModel में ये बदलाव, पूरी जानकारी देने के लिए करते हैं, ताकि आपके ऐप्लिकेशन में मौजूद सभी डेटा में LiveData का इस्तेमाल हो.
ScoreViewModelमें,scoreवैरिएबल टाइप कोMutableLiveDataमें बदलें. इसे_scoreके तौर पर नाम बदलें और बैकिंग प्रॉपर्टी जोड़ें.
private val _score = MutableLiveData<Int>()
val score: LiveData<Int>
get() = _scoreScoreViewModelमें,initब्लॉक के अंदर,_scoreको शुरू करें. आपके पासinitब्लॉक में मौजूद लॉग को हटाने या छोड़ने का विकल्प होता है.
init {
_score.value = finalScore
}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 इंपोर्ट करें.
- अपना ऐप्लिकेशन चलाएं और गेम खेलें. ऐप्लिकेशन पहले की तरह ही काम करेगा. हालांकि, अब यह स्कोर को अपडेट करने के लिए
LiveDataऔर ऑब्ज़र्वर का इस्तेमाल करता है.
इस टास्क में, आपको स्कोर स्क्रीन में फिर से खेलें बटन जोड़ना है. साथ ही, LiveData इवेंट का इस्तेमाल करके, इसके क्लिक लिसनर को लागू करना है. यह बटन, स्कोर स्क्रीन से गेम स्क्रीन पर जाने के लिए एक इवेंट ट्रिगर करता है.
ऐप्लिकेशन के स्टार्टर कोड में फिर से खेलें बटन शामिल है, लेकिन यह बटन छिपा हुआ है.
res/layout/score_fragment.xmlमें,play_again_buttonबटन के लिए,visibilityएट्रिब्यूट की वैल्यू कोvisibleपर सेट करें.
<Button
android:id="@+id/play_again_button"
...
android:visibility="visible"
/>ScoreViewModelमें,LiveDataऑब्जेक्ट जोड़ें, ताकि_eventPlayAgainनाम काBooleanहोल्ड किया जा सके. इस ऑब्जेक्ट का इस्तेमाल, स्कोर स्क्रीन से गेम स्क्रीन पर जाने के लिएLiveDataइवेंट को सेव करने के लिए किया जाता है.
private val _eventPlayAgain = MutableLiveData<Boolean>()
val eventPlayAgain: LiveData<Boolean>
get() = _eventPlayAgainScoreViewModelमें, इवेंट को सेट और रीसेट करने के तरीके तय करें,_eventPlayAgain.
fun onPlayAgain() {
_eventPlayAgain.value = true
}
fun onPlayAgainComplete() {
_eventPlayAgain.value = false
}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 इंपोर्ट करें.
ScoreFragmentमें,onCreateView()के अंदर, PlayAgain बटन में क्लिक लिसनर जोड़ें औरviewModel.onPlayAgain()को कॉल करें.
binding.playAgainButton.setOnClickListener { viewModel.onPlayAgain() }- अपना ऐप्लिकेशन चलाएं और गेम खेलें. गेम खत्म होने पर, स्कोर स्क्रीन पर फ़ाइनल स्कोर और फिर से खेलें बटन दिखता है. 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के अंदरprivateMutableLiveDataका इस्तेमाल करें. साथ ही,ViewModelके बाहरLiveDataबैकिंग प्रॉपर्टी दिखाएं.
ऑब्ज़र्वेबल LiveData
LiveData, ऑब्ज़र्वर पैटर्न का पालन करता है. "ऑब्ज़र्वेबल"LiveDataऑब्जेक्ट होता है. वहीं, ऑब्ज़र्वर, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में मौजूद तरीके होते हैं, जैसे कि फ़्रैगमेंट. जब भीLiveDataमें रैप किया गया डेटा बदलता है, तब यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में मौजूद ऑब्ज़र्वर के तरीकों को सूचना दी जाती है.LiveDataको ऑब्ज़र्व किया जा सकने वाला बनाने के लिए,observe()तरीके का इस्तेमाल करके, ऑब्ज़र्वर (जैसे कि ऐक्टिविटी और फ़्रैगमेंट) मेंLiveDataके रेफ़रंस से एक ऑब्ज़र्वर ऑब्जेक्ट अटैच करें.- इस
LiveDataऑब्ज़र्वर पैटर्न का इस्तेमाल,ViewModelसे यूज़र इंटरफ़ेस (यूआई) कंट्रोलर तक कम्यूनिकेट करने के लिए किया जा सकता है.
Udacity का कोर्स:
Android डेवलपर का दस्तावेज़:
- ViewModel की खास जानकारी
- LiveData के बारे में खास जानकारी
MutableLiveData- ऐप्लिकेशन के आर्किटेक्चर की गाइड
अन्य:
- Kotlin में बैकिंग प्रॉपर्टी
इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:
- अगर ज़रूरी हो, तो होमवर्क असाइन करें.
- छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
- होमवर्क असाइनमेंट को ग्रेड दें.
शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.
अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.
इन सवालों के जवाब दें
पहला सवाल
ViewModel में सेव किए गए LiveData को कैसे इनकैप्सुलेट किया जाता है, ताकि बाहरी ऑब्जेक्ट डेटा को अपडेट किए बिना पढ़ सकें?
ViewModelऑब्जेक्ट में, डेटा के टाइप कोprivateLiveDataमें बदलें.MutableLiveDataटाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.ViewModelऑब्जेक्ट में, डेटा के टाइप कोprivateMutableLiveDataमें बदलें.LiveDataटाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.- यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में, डेटा के टाइप को
privateMutableLiveDataमें बदलें.LiveDataटाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें. ViewModelऑब्जेक्ट में, डेटा के डेटा टाइप कोLiveDataमें बदलें.LiveDataटाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.
दूसरा सवाल
LiveData, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर (जैसे कि फ़्रैगमेंट) को कब अपडेट करता है?
- फिर से शुरू की गई
- बैकग्राउंड में
- रोकी गई
- बंद किया गया
तीसरा सवाल
LiveData ऑब्ज़र्वर पैटर्न में, ऑब्ज़र्व किया जा सकने वाला आइटम क्या है (क्या ऑब्ज़र्व किया जाता है)?
- ऑब्ज़र्वर मेथड
LiveDataऑब्जेक्ट में मौजूद डेटा- यूज़र इंटरफ़ेस (यूआई) कंट्रोलर
ViewModelऑब्जेक्ट
अगला सबक शुरू करें:
इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Android Kotlin Fundamentals कोडलैब का लैंडिंग पेज देखें.




