यह कोडलैब, Android Kotlin Fundamentals कोर्स का हिस्सा है. अगर कोडलैब को क्रम से पूरा किया जाता है, तो आपको इस कोर्स से सबसे ज़्यादा फ़ायदा मिलेगा. कोर्स के सभी कोडलैब, Android Kotlin Fundamentals कोडलैब के लैंडिंग पेज पर दिए गए हैं.
शीर्षक स्क्रीन | गेम की स्क्रीन | स्कोर स्क्रीन |
परिचय
इस कोडलैब में, आपको Android आर्किटेक्चर के कॉम्पोनेंट में से एक, ViewModel
के बारे में जानकारी मिलेगी:
ViewModel
क्लास का इस्तेमाल, यूज़र इंटरफ़ेस (यूआई) से जुड़े डेटा को सेव करने और मैनेज करने के लिए किया जाता है. ऐसा लाइफ़साइकल के हिसाब से किया जाता है.ViewModel
क्लास की मदद से, डिवाइस के कॉन्फ़िगरेशन में होने वाले बदलावों के दौरान डेटा को सुरक्षित रखा जा सकता है. जैसे, स्क्रीन रोटेशन और कीबोर्ड की उपलब्धता में बदलाव.- कॉन्फ़िगरेशन में बदलाव होने पर भी,
ViewModel
ऑब्जेक्ट को इंस्टैंशिएट और वापस लाने के लिए,ViewModelFactory
क्लास का इस्तेमाल किया जाता है.
आपको पहले से क्या पता होना चाहिए
- Kotlin में बुनियादी Android ऐप्लिकेशन बनाने का तरीका.
- अपने ऐप्लिकेशन में नेविगेशन लागू करने के लिए, नेविगेशन ग्राफ़ का इस्तेमाल करने का तरीका.
- अपने ऐप्लिकेशन के डेस्टिनेशन के बीच नेविगेट करने और नेविगेशन डेस्टिनेशन के बीच डेटा भेजने के लिए, कोड जोड़ने का तरीका.
- ऐक्टिविटी और फ़्रैगमेंट की लाइफ़साइकल कैसे काम करती हैं.
- किसी ऐप्लिकेशन में लॉगिंग की जानकारी जोड़ने और Android Studio में Logcat का इस्तेमाल करके लॉग पढ़ने का तरीका.
आपको क्या सीखने को मिलेगा
- सुझाए गए Android ऐप्लिकेशन आर्किटेक्चर का इस्तेमाल कैसे करें.
- अपने ऐप्लिकेशन में
Lifecycle
,ViewModel
, औरViewModelFactory
क्लास का इस्तेमाल करने का तरीका. - डिवाइस के कॉन्फ़िगरेशन में बदलाव होने पर, यूज़र इंटरफ़ेस (यूआई) डेटा को बनाए रखने का तरीका.
- फ़ैक्ट्री मेथड डिज़ाइन पैटर्न क्या है और इसका इस्तेमाल कैसे किया जाता है.
- इंटरफ़ेस
ViewModelProvider.Factory
का इस्तेमाल करके,ViewModel
ऑब्जेक्ट बनाने का तरीका.
आपको क्या करना होगा
- ऐप्लिकेशन में
ViewModel
जोड़ें, ताकि ऐप्लिकेशन का डेटा सेव किया जा सके. इससे कॉन्फ़िगरेशन में बदलाव होने पर भी डेटा सुरक्षित रहेगा. - कंस्ट्रक्टर पैरामीटर के साथ
ViewModel
ऑब्जेक्ट को इंस्टैंशिएट करने के लिए,ViewModelFactory
और फ़ैक्ट्री-मेथड डिज़ाइन पैटर्न का इस्तेमाल करें.
पांचवें लेसन के कोडलैब में, आपको GuessTheWord ऐप्लिकेशन बनाना है. इसके लिए, आपको स्टार्टर कोड से शुरुआत करनी होगी. GuessTheWord, दो खिलाड़ियों वाला शराबी गेम है. इसमें खिलाड़ी मिलकर, ज़्यादा से ज़्यादा स्कोर हासिल करने की कोशिश करते हैं.
पहला खिलाड़ी, ऐप्लिकेशन में दिए गए शब्दों को देखता है और एक-एक करके उन पर ऐक्ट करता है. वह यह पक्का करता है कि दूसरा खिलाड़ी उन शब्दों को न देख पाए. दूसरा खिलाड़ी, शब्द का अनुमान लगाने की कोशिश करता है.
गेम खेलने के लिए, पहला खिलाड़ी डिवाइस पर ऐप्लिकेशन खोलता है और उसे एक शब्द दिखता है. उदाहरण के लिए, "गिटार". यह शब्द, नीचे दिए गए स्क्रीनशॉट में दिखाया गया है.
पहला खिलाड़ी, शब्द को बोलकर नहीं, बल्कि ऐक्टिंग करके बताता है.
- जब दूसरा खिलाड़ी शब्द का सही अनुमान लगा लेता है, तब पहला खिलाड़ी समझ गया बटन दबाता है. इससे स्कोर में एक पॉइंट जुड़ जाता है और अगला शब्द दिखता है.
- अगर दूसरा प्लेयर शब्द का अनुमान नहीं लगा पाता है, तो पहला प्लेयर छोड़ें बटन दबाता है. इससे शब्द की संख्या एक कम हो जाती है और अगले शब्द पर पहुंच जाता है.
- गेम खत्म करने के लिए, गेम खत्म करें बटन दबाएं. (यह सुविधा, सीरीज़ के पहले कोडलैब के स्टार्टर कोड में नहीं है.)
इस टास्क में, आपको स्टार्टर ऐप्लिकेशन डाउनलोड करके चलाना होगा. साथ ही, कोड की जांच करनी होगी.
पहला चरण: शुरू करें
- GuessTheWord का स्टार्टर कोड डाउनलोड करें और Android Studio में प्रोजेक्ट खोलें.
- ऐप्लिकेशन को Android डिवाइस या Emulator पर चलाएं.
- बटन पर टैप करें. ध्यान दें कि छोड़ें बटन पर क्लिक करने से, अगला शब्द दिखता है और स्कोर में एक अंक की कमी आती है. वहीं, ठीक है बटन पर क्लिक करने से, अगला शब्द दिखता है और स्कोर में एक अंक की बढ़ोतरी होती है. गेम खत्म करें बटन लागू नहीं किया गया है. इसलिए, इस पर टैप करने से कुछ नहीं होता.
दूसरा चरण: कोड की बारीकी से जाँच करना
- Android Studio में, कोड को एक्सप्लोर करें. इससे आपको यह समझने में मदद मिलेगी कि ऐप्लिकेशन कैसे काम करता है.
- नीचे दी गई फ़ाइलों को ज़रूर देखें. ये फ़ाइलें खास तौर पर ज़रूरी हैं.
MainActivity.kt
इस फ़ाइल में सिर्फ़ डिफ़ॉल्ट और टेंप्लेट से जनरेट किया गया कोड होता है.
res/layout/main_activity.xml
इस फ़ाइल में ऐप्लिकेशन का मुख्य लेआउट होता है. उपयोगकर्ता के ऐप्लिकेशन में नेविगेट करने पर, NavHostFragment
अन्य फ़्रैगमेंट होस्ट करता है.
यूज़र इंटरफ़ेस (यूआई) फ़्रैगमेंट्स
स्टार्टर कोड में तीन फ़्रैगमेंट हैं. ये com.example.android.guesstheword.screens
पैकेज के तहत तीन अलग-अलग पैकेज में मौजूद हैं:
- टाइटल स्क्रीन के लिए
title/TitleFragment
game/GameFragment
गेम की स्क्रीन के लिएscore/ScoreFragment
स्कोर स्क्रीन के लिए
screens/title/TitleFragment.kt
टाइटल फ़्रैगमेंट, ऐप्लिकेशन लॉन्च होने पर दिखने वाली पहली स्क्रीन होती है. गेम स्क्रीन पर जाने के लिए, Play बटन पर क्लिक हैंडलर सेट किया जाता है.
screens/game/GameFragment.kt
यह मुख्य फ़्रैगमेंट है, जहाँ गेम की ज़्यादातर गतिविधियाँ होती हैं:
- मौजूदा शब्द और मौजूदा स्कोर के लिए वैरिएबल तय किए जाते हैं.
resetList()
तरीके में तय की गईwordList
, गेम में इस्तेमाल किए जाने वाले शब्दों की एक सैंपल सूची है.onSkip()
तरीका, Skip बटन के लिए क्लिक हैंडलर है. इससे स्कोर में एक की कमी आती है. इसके बाद,nextWord()
तरीके का इस्तेमाल करके अगला शब्द दिखाया जाता है.onCorrect()
तरीका, ठीक है बटन के लिए क्लिक हैंडलर है. इस तरीके कोonSkip()
तरीके की तरह ही लागू किया जाता है. इन दोनों तरीकों में सिर्फ़ यह अंतर है कि इस तरीके में स्कोर से 1 घटाने के बजाय जोड़ा जाता है.
screens/score/ScoreFragment.kt
ScoreFragment
गेम की आखिरी स्क्रीन है. इसमें खिलाड़ी का फ़ाइनल स्कोर दिखता है. इस कोडलैब में, आपको इस स्क्रीन को दिखाने और फ़ाइनल स्कोर दिखाने के लिए, कोड लागू करना होगा.
res/navigation/main_navigation.xml
नेविगेशन ग्राफ़ दिखाता है कि फ़्रैगमेंट, नेविगेशन के ज़रिए कैसे कनेक्ट होते हैं:
- टाइटल फ़्रैगमेंट से, उपयोगकर्ता गेम फ़्रैगमेंट पर जा सकता है.
- गेम फ़्रैगमेंट से, उपयोगकर्ता स्कोर फ़्रैगमेंट पर जा सकता है.
- स्कोर फ़्रैगमेंट से, उपयोगकर्ता गेम फ़्रैगमेंट पर वापस जा सकता है.
इस टास्क में, आपको GuessTheWord स्टार्टर ऐप्लिकेशन में मौजूद समस्याओं का पता लगाना है.
- स्टार्टर कोड चलाएं और कुछ शब्दों के ज़रिए गेम खेलें. हर शब्द के बाद, छोड़ें या ठीक है पर टैप करें.
- अब गेम की स्क्रीन पर एक शब्द और मौजूदा स्कोर दिखता है. डिवाइस या एम्युलेटर को घुमाकर, स्क्रीन का ओरिएंटेशन बदलें. ध्यान दें कि मौजूदा स्कोर मिट जाएगा.
- गेम को कुछ और शब्दों में चलाएं. जब गेम की स्क्रीन पर कुछ स्कोर दिखे, तो ऐप्लिकेशन को बंद करें और फिर से खोलें. ध्यान दें कि गेम शुरू से शुरू होता है, क्योंकि ऐप्लिकेशन की स्थिति सेव नहीं की जाती है.
- कुछ शब्दों का इस्तेमाल करके गेम खेलें. इसके बाद, गेम खत्म करें बटन पर टैप करें. ध्यान दें कि कुछ नहीं होता है.
ऐप्लिकेशन में समस्याएं:
- स्टार्टर ऐप्लिकेशन, कॉन्फ़िगरेशन में बदलाव होने पर ऐप्लिकेशन की स्थिति को सेव और रीस्टोर नहीं करता. जैसे, डिवाइस के ओरिएंटेशन में बदलाव होने पर या ऐप्लिकेशन बंद होने और फिर से चालू होने पर.
onSaveInstanceState()
कॉलबैक का इस्तेमाल करके, इस समस्या को हल किया जा सकता है. हालांकि,onSaveInstanceState()
तरीके का इस्तेमाल करने के लिए, आपको बंडल में स्थिति को सेव करने के लिए अतिरिक्त कोड लिखना होगा. साथ ही, उस स्थिति को वापस पाने के लिए लॉजिक लागू करना होगा. साथ ही, इसमें कम से कम डेटा सेव किया जा सकता है. - जब उपयोगकर्ता गेम खत्म करें बटन पर टैप करता है, तब गेम की स्क्रीन, स्कोर स्क्रीन पर नहीं जाती.
इस कोडलैब में बताए गए ऐप्लिकेशन आर्किटेक्चर कॉम्पोनेंट का इस्तेमाल करके, इन समस्याओं को हल किया जा सकता है.
ऐप्लिकेशन का आर्किटेक्चर
ऐप्लिकेशन आर्किटेक्चर, आपके ऐप्लिकेशन की क्लास और उनके बीच के संबंधों को इस तरह से डिज़ाइन करने का एक तरीका है कि कोड व्यवस्थित हो, खास स्थितियों में बेहतर परफ़ॉर्म करे, और उस पर काम करना आसान हो. चार कोडलैब के इस सेट में, GuessTheWord ऐप्लिकेशन में किए गए सुधार, Android ऐप्लिकेशन के आर्किटेक्चर से जुड़े दिशा-निर्देशों के मुताबिक होते हैं. साथ ही, Android Architecture Components का इस्तेमाल किया जाता है. Android ऐप्लिकेशन का आर्किटेक्चर, MVVM (मॉडल-व्यू-व्यूमॉडल) आर्किटेक्चरल पैटर्न जैसा होता है.
GuessTheWord ऐप्लिकेशन, ज़िम्मेदारियों को अलग-अलग करने के डिज़ाइन सिद्धांत का पालन करता है. इसे क्लास में बांटा गया है. हर क्लास एक अलग समस्या को हल करती है. इस सबक के पहले कोडलैब में, आपको यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, ViewModel
, और ViewModelFactory
क्लास के साथ काम करना है.
यूज़र इंटरफ़ेस (यूआई) कंट्रोलर
यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, यूज़र इंटरफ़ेस (यूआई) पर आधारित क्लास होती है. जैसे, Activity
या Fragment
. यूआई कंट्रोलर में सिर्फ़ ऐसा लॉजिक होना चाहिए जो यूज़र इंटरफ़ेस (यूआई) और ऑपरेटिंग सिस्टम के इंटरैक्शन को मैनेज करता हो. जैसे, व्यू दिखाना और उपयोगकर्ता के इनपुट को कैप्चर करना. यूज़र इंटरफ़ेस कंट्रोलर में फ़ैसले लेने का लॉजिक न डालें. जैसे, वह लॉजिक जो यह तय करता है कि कौन सा टेक्स्ट दिखाना है.
GuessTheWord के स्टार्टर कोड में, यूज़र इंटरफ़ेस कंट्रोलर तीन फ़्रैगमेंट हैं: GameFragment
, ScoreFragment,
, और TitleFragment
. "जिम्मेदारी अलग-अलग होना" डिज़ाइन सिद्धांत के मुताबिक, GameFragment
सिर्फ़ गेम के एलिमेंट को स्क्रीन पर दिखाने और यह जानने के लिए ज़िम्मेदार है कि उपयोगकर्ता ने बटन कब टैप किए. इसके अलावा, इसकी कोई और ज़िम्मेदारी नहीं है. जब उपयोगकर्ता किसी बटन पर टैप करता है, तो यह जानकारी GameViewModel
को पास कर दी जाती है.
ViewModel
ViewModel
में, ViewModel
से जुड़े फ़्रैगमेंट या ऐक्टिविटी में दिखाया जाने वाला डेटा होता है. ViewModel
, डेटा को यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में दिखाने के लिए, डेटा पर सामान्य कैलकुलेशन और ट्रांसफ़ॉर्मेशन कर सकता है. इस आर्किटेक्चर में, ViewModel
फ़ैसले लेने का काम करता है.GameViewModel
में स्कोर वैल्यू, शब्दों की सूची, और मौजूदा शब्द जैसे डेटा को सेव किया जाता है, क्योंकि यह डेटा स्क्रीन पर दिखाया जाना है. GameViewModel
में कारोबार से जुड़ा लॉजिक भी शामिल होता है. इससे डेटा की मौजूदा स्थिति का पता लगाने के लिए, सामान्य कैलकुलेशन की जाती हैं.
ViewModelFactory
ViewModelFactory
, कंस्ट्रक्टर पैरामीटर के साथ या उनके बिना ViewModel
ऑब्जेक्ट बनाता है.
बाद के कोडलैब में, आपको Android आर्किटेक्चर के उन कॉम्पोनेंट के बारे में जानकारी मिलेगी जो यूज़र इंटरफ़ेस (यूआई) कंट्रोलर और ViewModel
से जुड़े हैं.
ViewModel
क्लास को यूज़र इंटरफ़ेस (यूआई) से जुड़े डेटा को सेव और मैनेज करने के लिए डिज़ाइन किया गया है. इस ऐप्लिकेशन में, हर ViewModel
एक फ़्रैगमेंट से जुड़ा होता है.
इस टास्क में, आपको अपने ऐप्लिकेशन में पहला ViewModel
जोड़ना है. यह GameFragment
के लिए GameViewModel
है. आपको यह भी पता चलता है कि ViewModel
का लाइफ़साइकल से जुड़ा होना क्या मायने रखता है.
पहला चरण: GameViewModel क्लास जोड़ना
build.gradle(module:app)
फ़ाइल खोलें.dependencies
ब्लॉक में,ViewModel
के लिए Gradle डिपेंडेंसी जोड़ें.
अगर लाइब्रेरी के सबसे नए वर्शन का इस्तेमाल किया जाता है, तो समाधान ऐप्लिकेशन को उम्मीद के मुताबिक कंपाइल करना चाहिए. अगर ऐसा नहीं होता है, तो समस्या हल करने की कोशिश करें या नीचे दिए गए वर्शन पर वापस जाएं.
//ViewModel
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
- पैकेज
screens/game/
फ़ोल्डर में,GameViewModel
नाम की नई Kotlin क्लास बनाएं. GameViewModel
क्लास को ऐब्स्ट्रैक्ट क्लासViewModel
से बढ़ाएं.ViewModel
को लाइफ़साइकल के बारे में कैसे पता चलता है, इसे बेहतर तरीके से समझने के लिएlog
स्टेटमेंट के साथinit
ब्लॉक जोड़ें.
class GameViewModel : ViewModel() {
init {
Log.i("GameViewModel", "GameViewModel created!")
}
}
दूसरा चरण: onCleared() को बदलें और लॉगिंग जोड़ें
जब इससे जुड़ा फ़्रैगमेंट अलग हो जाता है या ऐक्टिविटी खत्म हो जाती है, तब ViewModel
डिस्ट्रॉय हो जाता है. ViewModel
को डिस्ट्रॉय करने से ठीक पहले, onCleared()
कॉलबैक को कॉल किया जाता है, ताकि संसाधनों को क्लीन अप किया जा सके.
GameViewModel
क्लास में,onCleared()
तरीके को बदलें.GameViewModel
के लाइफ़साइकल को ट्रैक करने के लिए,onCleared()
के अंदर एक लॉग स्टेटमेंट जोड़ें.
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed!")
}
तीसरा चरण: GameViewModel को गेम फ़्रैगमेंट से जोड़ना
ViewModel
को यूज़र इंटरफ़ेस (यूआई) कंट्रोलर से जोड़ा जाना चाहिए. दोनों को जोड़ने के लिए, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में ViewModel
का रेफ़रंस बनाया जाता है.
इस चरण में, आपको GameFragment
के अंदर मौजूद GameViewModel
का रेफ़रंस बनाना होता है.
GameFragment
क्लास में, टॉप लेवल परGameViewModel
टाइप का फ़ील्ड, क्लास वैरिएबल के तौर पर जोड़ें.
private lateinit var viewModel: GameViewModel
चौथा चरण: ViewModel को शुरू करना
कॉन्फ़िगरेशन में बदलाव होने पर, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर फिर से बनाए जाते हैं. जैसे, स्क्रीन रोटेशन के दौरान फ़्रैगमेंट फिर से बनाए जाते हैं. हालांकि, ViewModel
इंस्टेंस बचे रहते हैं. अगर ViewModel
क्लास का इस्तेमाल करके ViewModel
इंस्टेंस बनाया जाता है, तो फ़्रैगमेंट को फिर से बनाने पर हर बार एक नया ऑब्जेक्ट बनता है. इसके बजाय, ViewModelProvider
का इस्तेमाल करके ViewModel
इंस्टेंस बनाएं.
ViewModelProvider
कैसे काम करता है:
ViewModelProvider
फ़ंक्शन, मौजूदाViewModel
को दिखाता है. अगर कोईViewModel
मौजूद नहीं है, तो यह एक नयाViewModel
बनाता है.ViewModelProvider
दिए गए स्कोप (कोई गतिविधि या फ़्रैगमेंट) के साथ मिलकरViewModel
इंस्टेंस बनाता है.- स्कोप के हिसाब से जितना ज़्यादा हो सके उतने समय तक, बनाई गई
ViewModel
को सेव रखा जाता है. उदाहरण के लिए, अगर स्कोप कोई फ़्रैगमेंट है, तोViewModel
तब तक बना रहता है, जब तक फ़्रैगमेंट अलग नहीं हो जाता.
ViewModelProvider
बनाने के लिए, ViewModelProviders.of()
तरीके का इस्तेमाल करके ViewModel
को शुरू करें:
GameFragment
क्लास में,viewModel
वैरिएबल को शुरू करें. इस कोड कोonCreateView()
में, बाइंडिंग वैरिएबल की परिभाषा के बाद डालें.ViewModelProviders.of()
तरीके का इस्तेमाल करें. साथ ही, इससे जुड़ेGameFragment
कॉन्टेक्स्ट औरGameViewModel
क्लास को पास करें.ViewModel
ऑब्जेक्ट को शुरू करने से पहले,ViewModelProviders.of()
तरीके के कॉल को लॉग करने के लिए, एक लॉग स्टेटमेंट जोड़ें.
Log.i("GameFragment", "Called ViewModelProviders.of")
viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)
- ऐप्लिकेशन चलाएं. Android Studio में, Logcat पैनल खोलें और
Game
पर फ़िल्टर करें. अपने डिवाइस या एम्युलेटर पर, Play बटन पर टैप करें. गेम की स्क्रीन खुलती है.
Logcat में दिखाए गए तरीके के मुताबिक,GameFragment
काonCreateView()
तरीका,ViewModelProviders.of()
तरीके को कॉल करकेGameViewModel
बनाता है. आपनेGameFragment
औरGameViewModel
में जो लॉगिंग स्टेटमेंट जोड़े हैं वे Logcat में दिखते हैं.
- अपने डिवाइस या एम्युलेटर पर, ऑटो-रोटेट की सेटिंग चालू करें. इसके बाद, स्क्रीन ओरिएंटेशन को कुछ बार बदलें.
GameFragment
को हर बार डिस्ट्रॉय और फिर से बनाया जाता है. इसलिए,ViewModelProviders.of()
को हर बार कॉल किया जाता है. हालांकि,GameViewModel
को सिर्फ़ एक बार बनाया जाता है. साथ ही, इसे हर कॉल के लिए फिर से नहीं बनाया जाता है और न ही मिटाया जाता है.
I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of
- गेम से बाहर निकलें या गेम के फ़्रैगमेंट से बाहर निकलें.
GameFragment
को खत्म कर दिया गया है. इससे जुड़ाGameViewModel
भी डिस्ट्रॉय हो जाता है और कॉलबैकonCleared()
को कॉल किया जाता है.
I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel created! I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameFragment: Called ViewModelProviders.of I/GameViewModel: GameViewModel destroyed!
ViewModel
कॉन्फ़िगरेशन में किए गए बदलावों के बाद भी बना रहता है. इसलिए, यह ऐसे डेटा के लिए सबसे सही जगह है जिसे कॉन्फ़िगरेशन में किए गए बदलावों के बाद भी बनाए रखना है:
- स्क्रीन पर दिखने वाला डेटा और उस डेटा को प्रोसेस करने वाला कोड,
ViewModel
में डालें. ViewModel
में कभी भी फ़्रैगमेंट, ऐक्टिविटी या व्यू के रेफ़रंस नहीं होने चाहिए, क्योंकि कॉन्फ़िगरेशन में बदलाव होने पर ऐक्टिविटी, फ़्रैगमेंट, और व्यू नहीं रहते.
तुलना के लिए, यहां बताया गया है कि ViewModel
जोड़ने से पहले और ViewModel
जोड़ने के बाद, स्टार्टर ऐप्लिकेशन में GameFragment
यूज़र इंटरफ़ेस (यूआई) डेटा को कैसे मैनेज किया जाता है:
ViewModel
जोड़ने से पहले:
जब ऐप्लिकेशन के कॉन्फ़िगरेशन में बदलाव होता है, जैसे कि स्क्रीन रोटेशन, तो गेम फ़्रैगमेंट खत्म हो जाता है और फिर से बनाया जाता है. डेटा मिट जाता है.ViewModel
जोड़ने और गेम फ़्रैगमेंट के यूज़र इंटरफ़ेस (यूआई) डेटा कोViewModel
में ले जाने के बाद:
अब फ़्रैगमेंट को दिखाने के लिए ज़रूरी सारा डेटाViewModel
में मौजूद है. जब ऐप्लिकेशन के कॉन्फ़िगरेशन में बदलाव होता है, तबViewModel
बना रहता है और डेटा सेव रहता है.
इस टास्क में, ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) डेटा को GameViewModel
क्लास में ले जाया जाता है. साथ ही, डेटा को प्रोसेस करने के तरीके भी बताए जाते हैं. ऐसा इसलिए किया जाता है, ताकि कॉन्फ़िगरेशन में बदलाव होने पर भी डेटा बना रहे.
पहला चरण: डेटा फ़ील्ड और डेटा प्रोसेसिंग को ViewModel में ले जाना
नीचे दिए गए डेटा फ़ील्ड और तरीकों को GameFragment
से GameViewModel
में ले जाएं:
word
,score
, औरwordList
डेटा फ़ील्ड को दूसरी जगह ले जाएं. पक्का करें किword
औरscore
,private
न हों.
बाइंडिंग वैरिएबलGameFragmentBinding
को न हटाएं, क्योंकि इसमें व्यू के रेफ़रंस होते हैं. इस वैरिएबल का इस्तेमाल लेआउट को बड़ा करने, क्लिक लिसनर सेट अप करने, और स्क्रीन पर डेटा दिखाने के लिए किया जाता है. ये सभी काम फ़्रैगमेंट के होते हैं.resetList()
औरnextWord()
तरीकों को मूव करें. इन तरीकों से यह तय किया जाता है कि स्क्रीन पर कौनसे शब्द दिखाए जाएं.onCreateView()
तरीके के अंदर से,resetList()
औरnextWord()
को कॉल करने के तरीकों कोGameViewModel
केinit
ब्लॉक में ले जाएं.
ये तरीकेinit
ब्लॉक में होने चाहिए, क्योंकिViewModel
के बनने पर आपको शब्दों की सूची रीसेट करनी चाहिए, न कि हर बार फ़्रैगमेंट बनने पर.GameFragment
केinit
ब्लॉक में जाकर, लॉग स्टेटमेंट मिटाया जा सकता है.
GameFragment
में मौजूद onSkip()
और onCorrect()
क्लिक हैंडलर में, डेटा को प्रोसेस करने और यूज़र इंटरफ़ेस (यूआई) को अपडेट करने का कोड होता है. यूज़र इंटरफ़ेस (यूआई) को अपडेट करने वाला कोड, फ़्रैगमेंट में ही रहना चाहिए. हालांकि, डेटा को प्रोसेस करने वाले कोड को ViewModel
में ले जाना होगा.
फ़िलहाल, दोनों जगहों पर एक जैसे तरीके इस्तेमाल करें:
GameFragment
सेonSkip()
औरonCorrect()
तरीकों कोGameViewModel
में कॉपी करें.GameViewModel
में, पक्का करें किonSkip()
औरonCorrect()
तरीकेprivate
न हों, क्योंकि आपको फ़्रैगमेंट से इन तरीकों का रेफ़रंस देना होगा.
रीफ़ैक्टर करने के बाद, GameViewModel
क्लास का कोड यहां दिया गया है:
class GameViewModel : ViewModel() {
// The current word
var word = ""
// The current score
var score = 0
// The list of words - the front of the list is the next word to guess
private lateinit var wordList: MutableList<String>
/**
* Resets the list of words and randomizes the order
*/
private fun resetList() {
wordList = mutableListOf(
"queen",
"hospital",
"basketball",
"cat",
"change",
"snail",
"soup",
"calendar",
"sad",
"desk",
"guitar",
"home",
"railway",
"zebra",
"jelly",
"car",
"crow",
"trade",
"bag",
"roll",
"bubble"
)
wordList.shuffle()
}
init {
resetList()
nextWord()
Log.i("GameViewModel", "GameViewModel created!")
}
/**
* Moves to the next word in the list
*/
private fun nextWord() {
if (!wordList.isEmpty()) {
//Select and remove a word from the list
word = wordList.removeAt(0)
}
updateWordText()
updateScoreText()
}
/** Methods for buttons presses **/
fun onSkip() {
if (!wordList.isEmpty()) {
score--
}
nextWord()
}
fun onCorrect() {
if (!wordList.isEmpty()) {
score++
}
nextWord()
}
override fun onCleared() {
super.onCleared()
Log.i("GameViewModel", "GameViewModel destroyed!")
}
}
रीफ़ैक्टर करने के बाद, GameFragment
क्लास का कोड यहां दिया गया है:
/**
* Fragment where the game is played
*/
class GameFragment : Fragment() {
private lateinit var binding: GameFragmentBinding
private lateinit var viewModel: GameViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate view and obtain an instance of the binding class
binding = DataBindingUtil.inflate(
inflater,
R.layout.game_fragment,
container,
false
)
Log.i("GameFragment", "Called ViewModelProviders.of")
viewModel = ViewModelProviders.of(this).get(GameViewModel::class.java)
binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
updateScoreText()
updateWordText()
return binding.root
}
/** Methods for button click handlers **/
private fun onSkip() {
if (!wordList.isEmpty()) {
score--
}
nextWord()
}
private fun onCorrect() {
if (!wordList.isEmpty()) {
score++
}
nextWord()
}
/** Methods for updating the UI **/
private fun updateWordText() {
binding.wordText.text = word
}
private fun updateScoreText() {
binding.scoreText.text = score.toString()
}
}
दूसरा चरण: GameFragment में क्लिक हैंडलर और डेटा फ़ील्ड के रेफ़रंस अपडेट करना
GameFragment
में,onSkip()
औरonCorrect()
तरीके अपडेट करें. स्कोर अपडेट करने के लिए कोड हटाएं. इसके बजाय,viewModel
पर, इससे जुड़ेonSkip()
औरonCorrect()
तरीकों को कॉल करें.- आपने
nextWord()
तरीके कोViewModel
में ले जाया है. इसलिए, गेम फ़्रैगमेंट अब इसे ऐक्सेस नहीं कर सकता.GameFragment
में,onSkip()
औरonCorrect()
तरीकों में,nextWord()
को कॉल करने की जगहupdateScoreText()
औरupdateWordText()
को कॉल करें. इन तरीकों से, स्क्रीन पर डेटा दिखता है.
private fun onSkip() {
viewModel.onSkip()
updateWordText()
updateScoreText()
}
private fun onCorrect() {
viewModel.onCorrect()
updateScoreText()
updateWordText()
}
GameFragment
में,score
औरword
वैरिएबल को अपडेट करकेGameViewModel
वैरिएबल का इस्तेमाल करें. ऐसा इसलिए, क्योंकि ये वैरिएबल अबGameViewModel
में हैं.
private fun updateWordText() {
binding.wordText.text = viewModel.word
}
private fun updateScoreText() {
binding.scoreText.text = viewModel.score.toString()
}
GameViewModel
में,nextWord()
तरीके के अंदर,updateWordText()
औरupdateScoreText()
तरीकों के कॉल हटाएं. अब इन तरीकों कोGameFragment
से कॉल किया जा रहा है.- ऐप्लिकेशन बनाएं और पक्का करें कि उसमें कोई गड़बड़ी न हो. अगर आपको गड़बड़ियां दिखती हैं, तो प्रोजेक्ट को क्लीन करें और फिर से बनाएं.
- कुछ शब्दों का इस्तेमाल करके, ऐप्लिकेशन को चलाओ और गेम खेलो. गेम स्क्रीन पर रहते हुए, डिवाइस को घुमाएं. ध्यान दें कि ओरिएंटेशन बदलने के बाद भी, मौजूदा स्कोर और मौजूदा शब्द बने रहते हैं.
बहुत बढ़िया! अब आपके ऐप्लिकेशन का सारा डेटा ViewModel
में सेव किया जाता है. इसलिए, कॉन्फ़िगरेशन में बदलाव होने पर भी यह डेटा बना रहता है.
इस टास्क में, आपको End Game बटन के लिए क्लिक लिसनर लागू करना है.
GameFragment
में,onEndGame()
नाम की एक नई विधि जोड़ें. जब उपयोगकर्ता गेम खत्म करें बटन पर टैप करेगा, तबonEndGame()
तरीके को कॉल किया जाएगा.
private fun onEndGame() {
}
GameFragment
में,onCreateView()
तरीके के अंदर, वह कोड ढूंढें जो ठीक है और अभी नहीं बटन के लिए क्लिक लिसनर सेट करता है. इन दोनों लाइनों के ठीक नीचे, End Game बटन के लिए क्लिक लिसनर सेट करें. बाइंडिंग वैरिएबल,binding
का इस्तेमाल करें. क्लिक लिसनर के अंदर,onEndGame()
वाले तरीके को कॉल करें.
binding.endGameButton.setOnClickListener { onEndGame() }
GameFragment
में, ऐप्लिकेशन को स्कोर स्क्रीन पर ले जाने के लिए,gameFinished()
नाम का एक तरीका जोड़ें. Safe Args का इस्तेमाल करके, स्कोर को आर्ग्युमेंट के तौर पर पास करें.
/**
* Called when the game is finished
*/
private fun gameFinished() {
Toast.makeText(activity, "Game has just finished", Toast.LENGTH_SHORT).show()
val action = GameFragmentDirections.actionGameToScore()
action.score = viewModel.score
NavHostFragment.findNavController(this).navigate(action)
}
onEndGame()
तरीके में,gameFinished()
तरीके को कॉल करें.
private fun onEndGame() {
gameFinished()
}
- ऐप्लिकेशन चलाएं, गेम खेलें, और कुछ शब्दों को दोहराएं. गेम खत्म करें बटन पर टैप करें. ध्यान दें कि ऐप्लिकेशन, स्कोर वाली स्क्रीन पर पहुंच जाता है, लेकिन फ़ाइनल स्कोर नहीं दिखता. अगले टास्क में इस समस्या को ठीक किया जा सकता है.
जब उपयोगकर्ता गेम खत्म करता है, तो ScoreFragment
स्कोर नहीं दिखाता है. आपको ScoreFragment
को स्कोर दिखाने के लिए, ViewModel
की ज़रूरत है. फ़ैक्ट्री मेथड पैटर्न का इस्तेमाल करके, ViewModel
के शुरू होने के दौरान स्कोर की वैल्यू पास की जाएगी.
फ़ैक्ट्री मेथड पैटर्न, एक क्रिएशनल डिज़ाइन पैटर्न है. यह ऑब्जेक्ट बनाने के लिए फ़ैक्ट्री मेथड का इस्तेमाल करता है. फ़ैक्ट्री मेथड एक ऐसा मेथड होता है जो उसी क्लास का इंस्टेंस दिखाता है.
इस टास्क में, आपको स्कोर फ़्रैगमेंट के लिए पैरामीटर वाला कंस्ट्रक्टर और ViewModel
को इंस्टैंशिएट करने के लिए फ़ैक्ट्री मेथड के साथ एक ViewModel
बनाना होगा.
score
पैकेज में,ScoreViewModel
नाम की नई Kotlin क्लास बनाएं. यह क्लास, स्कोर फ़्रैगमेंट के लिएViewModel
होगी.ScoreViewModel
क्लास कोViewModel.
से बढ़ाएं. फ़ाइनल स्कोर के लिए कंस्ट्रक्टर पैरामीटर जोड़ें. लॉग स्टेटमेंट के साथinit
ब्लॉक जोड़ें.- फ़ाइनल स्कोर को सेव करने के लिए,
ScoreViewModel
क्लास मेंscore
नाम का एक वैरिएबल जोड़ें.
class ScoreViewModel(finalScore: Int) : ViewModel() {
// The final score
var score = finalScore
init {
Log.i("ScoreViewModel", "Final score is $finalScore")
}
}
score
पैकेज में,ScoreViewModelFactory
नाम की एक और Kotlin क्लास बनाएं. यह क्लास,ScoreViewModel
ऑब्जेक्ट को इंस्टैंशिएट करने के लिए ज़िम्मेदार होगी.ScoreViewModelFactory
क्लास कोViewModelProvider.Factory
से बढ़ाएं. फ़ाइनल स्कोर के लिए कंस्ट्रक्टर पैरामीटर जोड़ें.
class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {
}
ScoreViewModelFactory
में, Android Studio, लागू नहीं किए गए ऐब्स्ट्रैक्ट मेंबर के बारे में गड़बड़ी दिखाता है. गड़बड़ी को ठीक करने के लिए,create()
तरीके को बदलें.create()
तरीके में, नए बनाए गएScoreViewModel
ऑब्जेक्ट को दिखाएं.
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
return ScoreViewModel(finalScore) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
ScoreFragment
में,ScoreViewModel
औरScoreViewModelFactory
के लिए क्लास वैरिएबल बनाएं.
private lateinit var viewModel: ScoreViewModel
private lateinit var viewModelFactory: ScoreViewModelFactory
ScoreFragment
में,onCreateView()
के अंदर,binding
वैरिएबल को शुरू करने के बाद,viewModelFactory
को शुरू करें.ScoreViewModelFactory
का इस्तेमाल करें. आर्ग्युमेंट बंडल से मिले फ़ाइनल स्कोर को,ScoreViewModelFactory()
के कंस्ट्रक्टर पैरामीटर के तौर पर पास करें.
viewModelFactory = ScoreViewModelFactory(ScoreFragmentArgs.fromBundle(arguments!!).score)
onCreateView(
में,viewModelFactory
को शुरू करने के बाद,viewModel
ऑब्जेक्ट को शुरू करें.ViewModelProviders.of()
वाले तरीके को कॉल करें. साथ ही, उससे जुड़े स्कोर फ़्रैगमेंट के कॉन्टेक्स्ट औरviewModelFactory
को पास करें. इससेviewModelFactory
क्लास.
में तय की गई फ़ैक्ट्री मेथड का इस्तेमाल करके,ScoreViewModel
ऑब्जेक्ट बनाया जाएगा
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ScoreViewModel::class.java)
onCreateView()
तरीके में,viewModel
को शुरू करने के बाद,scoreText
व्यू के टेक्स्ट कोScoreViewModel
में तय किए गए फ़ाइनल स्कोर पर सेट करें.
binding.scoreText.text = viewModel.score.toString()
- अपना ऐप्लिकेशन चलाएं और गेम खेलें. कुछ या सभी शब्दों को साइकल करें और गेम खत्म करें पर टैप करें. ध्यान दें कि स्कोर फ़्रैगमेंट में अब फ़ाइनल स्कोर दिख रहा है.
- ज़रूरी नहीं: Logcat में
ScoreViewModel
पर फ़िल्टर करके,ScoreViewModel
लॉग देखें. स्कोर की वैल्यू दिखनी चाहिए.
2019-02-07 10:50:18.328 com.example.android.guesstheword I/ScoreViewModel: Final score is 15
इस टास्क में, आपने ViewModel
का इस्तेमाल करने के लिए ScoreFragment
को लागू किया. आपने यह भी सीखा कि ViewModelFactory
इंटरफ़ेस का इस्तेमाल करके, ViewModel
के लिए पैरामीटर वाला कंस्ट्रक्टर कैसे बनाया जाता है.
बधाई हो! आपने Android आर्किटेक्चर के कॉम्पोनेंट, ViewModel
में से किसी एक का इस्तेमाल करने के लिए, अपने ऐप्लिकेशन के आर्किटेक्चर में बदलाव किया हो. आपने ऐप्लिकेशन के लाइफ़साइकल से जुड़ी समस्या हल कर दी है. अब गेम का डेटा, कॉन्फ़िगरेशन में हुए बदलावों के बाद भी सुरक्षित रहता है. आपने यह भी सीखा कि ViewModelFactory
इंटरफ़ेस का इस्तेमाल करके, ViewModel
बनाने के लिए पैरामीटर वाला कंस्ट्रक्टर कैसे बनाया जाता है.
Android Studio प्रोजेक्ट: GuessTheWord
- Android के ऐप्लिकेशन आर्किटेक्चर के दिशा-निर्देशों में, अलग-अलग ज़िम्मेदारियों वाली क्लास को अलग करने का सुझाव दिया गया है.
- यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, यूज़र इंटरफ़ेस (यूआई) पर आधारित क्लास होती है. जैसे,
Activity
याFragment
. यूज़र इंटरफ़ेस (यूआई) कंट्रोलर में सिर्फ़ ऐसा लॉजिक होना चाहिए जो यूज़र इंटरफ़ेस (यूआई) और ऑपरेटिंग सिस्टम के इंटरैक्शन को मैनेज करता हो. इनमें यूज़र इंटरफ़ेस (यूआई) में दिखने वाला डेटा नहीं होना चाहिए. उस डेटा कोViewModel
में डालें. ViewModel
क्लास, यूज़र इंटरफ़ेस (यूआई) से जुड़े डेटा को सेव और मैनेज करती है.ViewModel
क्लास की मदद से, कॉन्फ़िगरेशन में हुए बदलावों के दौरान डेटा को सुरक्षित रखा जा सकता है. जैसे, स्क्रीन रोटेशन.ViewModel
, Android आर्किटेक्चर कॉम्पोनेंट में से एक है.ViewModelProvider.Factory
एक इंटरफ़ेस है. इसका इस्तेमाल करके,ViewModel
ऑब्जेक्ट बनाया जा सकता है.
नीचे दी गई टेबल में, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर की तुलना उन ViewModel
इंस्टेंस से की गई है जिनमें उनके लिए डेटा होता है:
यूज़र इंटरफ़ेस (यूआई) कंट्रोलर | ViewModel |
यूज़र इंटरफ़ेस (यूआई) कंट्रोलर का एक उदाहरण, |
|
इसमें यूज़र इंटरफ़ेस (यूआई) में दिखाने के लिए कोई डेटा नहीं होता. | इसमें वह डेटा होता है जिसे यूज़र इंटरफ़ेस (यूआई) कंट्रोलर, यूज़र इंटरफ़ेस (यूआई) में दिखाता है. |
इसमें डेटा दिखाने के लिए कोड होता है. साथ ही, इसमें उपयोगकर्ता के इवेंट का कोड होता है. जैसे, क्लिक लिसनर. | इसमें डेटा प्रोसेसिंग के लिए कोड होता है. |
कॉन्फ़िगरेशन में हर बदलाव के दौरान, इसे डिस्ट्रॉय और फिर से बनाया जाता है. | यह सिर्फ़ तब बंद होता है, जब इससे जुड़ा यूज़र इंटरफ़ेस (यूआई) कंट्रोलर हमेशा के लिए बंद हो जाता है. जैसे, किसी ऐक्टिविटी के लिए, जब ऐक्टिविटी खत्म हो जाती है या किसी फ़्रैगमेंट के लिए, जब फ़्रैगमेंट अलग हो जाता है. |
इसमें व्यू शामिल हैं. | इसमें कभी भी ऐक्टिविटी, फ़्रैगमेंट या व्यू के रेफ़रंस नहीं होने चाहिए, क्योंकि कॉन्फ़िगरेशन में बदलाव होने पर ये नहीं रहते. हालांकि, |
इसमें, उससे जुड़े | इसमें, यूज़र इंटरफ़ेस (यूआई) कंट्रोलर का कोई रेफ़रंस नहीं होता. |
Udacity का कोर्स:
Android डेवलपर का दस्तावेज़:
- ViewModel की खास जानकारी
- लाइफ़साइकल की जानकारी वाले कॉम्पोनेंट की मदद से लाइफ़साइकल मैनेज करना
- ऐप्लिकेशन के आर्किटेक्चर की गाइड
ViewModelProvider
ViewModelProvider.Factory
अन्य:
- MVVM (मॉडल-व्यू-व्यूमॉडल) आर्किटेक्चरल पैटर्न.
- गौर करने लायक अलग-अलग बातों (एसओसी) से जुड़ा डिज़ाइन सिद्धांत
- फ़ैक्ट्री मेथड पैटर्न
इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:
- अगर ज़रूरी हो, तो होमवर्क असाइन करें.
- छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
- होमवर्क असाइनमेंट को ग्रेड दें.
शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.
अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.
इन सवालों के जवाब दें
पहला सवाल
डिवाइस के कॉन्फ़िगरेशन में बदलाव के दौरान डेटा को नुकसान से बचाने के लिए, आपको ऐप्लिकेशन का डेटा किस क्लास में सेव करना चाहिए?
ViewModel
LiveData
Fragment
Activity
दूसरा सवाल
ViewModel
में कभी भी फ़्रैगमेंट, ऐक्टिविटी या व्यू के रेफ़रंस नहीं होने चाहिए. सही या गलत?
- सही
- गलत
तीसरा सवाल
ViewModel
को कब मिटाया जाता है?
- जब डिवाइस के ओरिएंटेशन में बदलाव के दौरान, उससे जुड़ा यूज़र इंटरफ़ेस (यूआई) कंट्रोलर खत्म हो जाता है और फिर से बन जाता है.
- ओरिएंटेशन बदलने पर.
- जब उससे जुड़ा यूज़र इंटरफ़ेस (यूआई) कंट्रोलर खत्म हो जाता है (अगर यह कोई ऐक्टिविटी है) या अलग हो जाता है (अगर यह कोई फ़्रैगमेंट है).
- जब उपयोगकर्ता, 'वापस जाएं' बटन दबाता है.
चौथा सवाल
ViewModelFactory
इंटरफ़ेस का इस्तेमाल किस लिए किया जाता है?
ViewModel
ऑब्जेक्ट को इंस्टैंशिएट करना.- ओरिएंटेशन में बदलाव होने पर डेटा को बनाए रखना.
- स्क्रीन पर दिख रहे डेटा को रीफ़्रेश किया जा रहा है.
- ऐप्लिकेशन का डेटा बदलने पर सूचनाएं पाना.
अगला सबक शुरू करें:
इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Android Kotlin Fundamentals कोडलैब का लैंडिंग पेज देखें.