यह कोडलैब, 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() = _score
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)
}
...
}
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
में,Boolean
MutableLiveData
ऑब्जेक्ट बनाएं. इसका नाम_eventGameFinish
रखें. इस ऑब्जेक्ट में, गेम खत्म होने का इवेंट शामिल होगा._eventGameFinish
ऑब्जेक्ट को शुरू करने के बाद,eventGameFinish
नाम की बैकिंग प्रॉपर्टी बनाएं और उसे शुरू करें.
// Event which triggers the end of the game
private val _eventGameFinish = MutableLiveData<Boolean>()
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish
GameViewModel
में,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() = _score
ScoreViewModel
में,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() = _eventPlayAgain
ScoreViewModel
में, इवेंट को सेट और रीसेट करने के तरीके तय करें,_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
के अंदरprivate
MutableLiveData
का इस्तेमाल करें. साथ ही,ViewModel
के बाहरLiveData
बैकिंग प्रॉपर्टी दिखाएं.
ऑब्ज़र्वेबल LiveData
LiveData
, ऑब्ज़र्वर पैटर्न का पालन करता है. "ऑब्ज़र्वेबल"LiveData
ऑब्जेक्ट होता है. वहीं, ऑब्ज़र्वर, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में मौजूद तरीके होते हैं, जैसे कि फ़्रैगमेंट. जब भीLiveData
में रैप किया गया डेटा बदलता है, तब यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में मौजूद ऑब्ज़र्वर के तरीकों को सूचना दी जाती है.LiveData
को ऑब्ज़र्व किया जा सकने वाला बनाने के लिए,observe()
तरीके का इस्तेमाल करके, ऑब्ज़र्वर (जैसे कि ऐक्टिविटी और फ़्रैगमेंट) मेंLiveData
के रेफ़रंस से एक ऑब्ज़र्वर ऑब्जेक्ट अटैच करें.- इस
LiveData
ऑब्ज़र्वर पैटर्न का इस्तेमाल,ViewModel
से यूज़र इंटरफ़ेस (यूआई) कंट्रोलर तक कम्यूनिकेट करने के लिए किया जा सकता है.
Udacity का कोर्स:
Android डेवलपर का दस्तावेज़:
- ViewModel की खास जानकारी
- LiveData के बारे में खास जानकारी
MutableLiveData
- ऐप्लिकेशन के आर्किटेक्चर की गाइड
अन्य:
- Kotlin में बैकिंग प्रॉपर्टी
इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:
- अगर ज़रूरी हो, तो होमवर्क असाइन करें.
- छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
- होमवर्क असाइनमेंट को ग्रेड दें.
शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.
अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.
इन सवालों के जवाब दें
पहला सवाल
ViewModel
में सेव किए गए LiveData
को कैसे इनकैप्सुलेट किया जाता है, ताकि बाहरी ऑब्जेक्ट डेटा को अपडेट किए बिना पढ़ सकें?
ViewModel
ऑब्जेक्ट में, डेटा के टाइप कोprivate
LiveData
में बदलें.MutableLiveData
टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.ViewModel
ऑब्जेक्ट में, डेटा के टाइप कोprivate
MutableLiveData
में बदलें.LiveData
टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.- यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में, डेटा के टाइप को
private
MutableLiveData
में बदलें.LiveData
टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें. ViewModel
ऑब्जेक्ट में, डेटा के डेटा टाइप कोLiveData
में बदलें.LiveData
टाइप के सिर्फ़ पढ़ने के लिए उपलब्ध डेटा को दिखाने के लिए, बैकिंग प्रॉपर्टी का इस्तेमाल करें.
दूसरा सवाल
LiveData
, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर (जैसे कि फ़्रैगमेंट) को कब अपडेट करता है?
- फिर से शुरू की गई
- बैकग्राउंड में
- रोकी गई
- बंद किया गया
तीसरा सवाल
LiveData
ऑब्ज़र्वर पैटर्न में, ऑब्ज़र्व किया जा सकने वाला आइटम क्या है (क्या ऑब्ज़र्व किया जाता है)?
- ऑब्ज़र्वर मेथड
LiveData
ऑब्जेक्ट में मौजूद डेटा- यूज़र इंटरफ़ेस (यूआई) कंट्रोलर
ViewModel
ऑब्जेक्ट
अगला सबक शुरू करें:
इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Android Kotlin Fundamentals कोडलैब का लैंडिंग पेज देखें.