क्रिप्टोफोलियो ऐप शृंखला - भाग 3
एक नया ऐप बनाना शुरू करते समय सबसे महत्वपूर्ण बात आर्किटेक्चर है। सबसे बड़ी गलती जो आप कर सकते हैं, वह है बिना किसी वास्तुकला शैली के।
हाल के वर्षों में एंड्रॉइड समुदाय के लिए आर्किटेक्चर पसंद का विषय काफी विवादास्पद रहा है। यहां तक कि Google ने भी इसमें शामिल होने का फैसला किया। 2017 में उन्होंने एंड्रॉइड आर्किटेक्चर घटकों को जारी करके मानकीकृत वास्तुकला के अपने दृष्टिकोण का प्रस्ताव दिया। इसका उद्देश्य डेवलपर्स के जीवन को आसान बनाना था।
इस पोस्ट में, मैं सबसे पहले चर्चा करने जा रहा हूं कि हमें अपने ऐप्स को आर्किटेक्ट करने की आवश्यकता क्यों है। हमारे पास जो विकल्प हैं, हम उसे कवर करेंगे। फिर हम सीखेंगे कि यह कैसे करना है। पहिया को फिर से खोजने के बजाय, हम Android टीम द्वारा प्रदान किए गए दिशानिर्देशों का उपयोग करेंगे।
मेरे लिए इसके बारे में जानकारी की कमी के कारण यह पोस्ट लिखना मेरे लिए सबसे कठिन था। पहले मुझे बड़ी तस्वीर देखने के लिए वास्तुकला के विषय का वास्तव में अच्छी तरह से अध्ययन करना पड़ा। अब मैं अपने निष्कर्ष आपके साथ साझा करने के लिए तैयार हूं।
श्रृंखला सामग्री
- परिचय:2018–2019 में एक आधुनिक Android ऐप बनाने का रोडमैप
- भाग 1:ठोस सिद्धांतों का परिचय
- भाग 2:अपना Android ऐप कैसे बनाना शुरू करें:मॉकअप, UI और XML लेआउट बनाना
- भाग 3:उस आर्किटेक्चर के बारे में सब कुछ:विभिन्न आर्किटेक्चर पैटर्न की खोज करना और उन्हें अपने ऐप में कैसे उपयोग करना है (आप यहां हैं)
- भाग 4:डैगर 2 के साथ अपने ऐप में डिपेंडेंसी इंजेक्शन कैसे लागू करें
- भाग 5:Retrofit, OkHttp, Gson, Glide और Coroutines का उपयोग करके RESTful Web Services को हैंडल करें
आपको ऐप आर्किटेक्चर की परवाह क्यों करनी चाहिए?
आम तौर पर जब आप एंड्रॉइड के साथ काम करना शुरू करते हैं तो आप गतिविधियों या टुकड़ों में अधिकांश मुख्य व्यावसायिक तर्क लिखना समाप्त कर देते हैं। यह मेरे सहित सभी नए Android डेवलपर के साथ होता है। सभी लघु ट्यूटोरियल और सभी नमूने ऐसा करने का सुझाव देते हैं। और वास्तव में, स्पष्टीकरण के लिए बनाए गए छोटे ऐप्स के लिए, यह काफी अच्छा काम करता है।
हालाँकि, एक वास्तविक ऐप पर ऐसा करने का प्रयास करें जो उपयोगकर्ताओं की आवश्यकताओं के अनुसार लगातार बदल रहा है और इसे नई सुविधाओं के साथ विस्तारित कर रहा है। जल्द ही आप देखेंगे कि आपका कोडिंग अनुभव अधिक से अधिक दर्दनाक होता जा रहा है। सब कुछ तथाकथित "गॉड क्लासेस" जैसे गतिविधियों या टुकड़ों द्वारा प्रबंधित किया जाता है। इनमें कोड की इतनी अधिक पंक्तियाँ होती हैं कि आप आसानी से खो जाते हैं।
मूल रूप से आपका सारा कोड स्पेगेटी की तरह दिखने लगता है जहाँ सब कुछ मिलाया जाता है। सभी अंग एक दूसरे पर निर्भर हैं। फिर जब व्यवसाय द्वारा नए परिवर्तनों की आवश्यकता होती है, तो आपके पास पूरी परियोजना के पुनर्निर्माण के अलावा कोई विकल्प नहीं रह जाता है। साथ ही यही वह बिंदु है जहां वास्तुकला के प्रश्न प्रकट होने लगते हैं।
क्या आपके कोड की संरचना करने का कोई बेहतर तरीका है?
बेशक वहाँ है! उच्च गुणवत्ता कोड की कुंजी ठोस सिद्धांतों का पालन करना है। मैंने इस बारे में अपनी पिछली पोस्ट में बात की थी (बिना किसी कारण के)। चिंताओं को अलग करने के लिए आपको कुछ आर्किटेक्चर पैटर्न भी लागू करना चाहिए। वास्तव में, चिंताओं को अलग करना आपका अंतिम लक्ष्य होना चाहिए। यह सबसे महत्वपूर्ण बिंदु है जो कोड गुणवत्ता को इंगित करता है। ऐप आर्किटेक्चर के लिए काफी कुछ पैटर्न हैं। क्लासिक थ्री टियर आर्किटेक्चर सबसे प्रसिद्ध हैं जैसे:
- एमवीसी:मॉडल-व्यू-कंट्रोलर
- एमवीपी:मॉडल-व्यू-प्रस्तुतकर्ता
- एमवीवीएम:मॉडल-व्यू-व्यूमॉडल
ये सभी पैटर्न मुख्य समान विचार का प्रतिनिधित्व करते हैं - आपके प्रोजेक्ट के कोड को इस तरह से संरचित करने के लिए कि यह विभिन्न सामान्य परतों से अलग हो। हर परत की अपनी जिम्मेदारी होती है। इसलिए आपका प्रोजेक्ट मॉड्यूलर बन जाता है:अलग-अलग कोड भाग अधिक परीक्षण योग्य होते हैं, और आपका ऐप निरंतर परिवर्तनों के लिए पर्याप्त लचीला होता है।
यदि हम प्रत्येक पैटर्न के बारे में व्यक्तिगत रूप से बात करें, तो विषय बहुत व्यापक हो जाता है। मैं आपको हर एक से सिर्फ इसलिए मिलवाने जा रहा हूँ ताकि आप मुख्य अंतरों को समझ सकें।
मॉडल-व्यू-कंट्रोलर (MVC) पैटर्न
यह पैटर्न पुराने समय में वापस लिया गया पहला एंड्रॉइड ऐप आर्किटेक्चर था। यह सुझाव देता है कि आप अपने कोड को 3 अलग-अलग परतों में विभाजित करें:
मॉडल - डेटा परत। नेटवर्क और डेटाबेस परतों के साथ व्यावसायिक तर्क और संचार को संभालने के लिए जिम्मेदार।
देखें - यूजर इंटरफेस (यूआई) परत। यह मॉडल के डेटा का एक सरल विज़ुअलाइज़ेशन है।
नियंत्रक — तर्क परत, उपयोगकर्ता के व्यवहार के बारे में सूचित करता है और आवश्यकतानुसार मॉडल को अद्यतन करता है।
यह एमवीसी स्कीमा है। इसमें हम देख सकते हैं कि नियंत्रक और दृश्य दोनों मॉडल पर निर्भर करते हैं। नियंत्रक डेटा को अद्यतन करता है। दृश्य डेटा प्राप्त करता है। हालांकि, मॉडल अलग है और यूआई से स्वतंत्र रूप से परीक्षण किया जा सकता है।
एमवीसी पैटर्न को लागू करने के कुछ तरीके हैं। यह काफी भ्रमित करने वाला है।
एक तब होता है जब गतिविधियाँ और अंश नियंत्रक की तरह कार्य करते हैं। वे डेटा को संसाधित करने और विचारों को अद्यतन करने के प्रभारी हैं। इस वास्तुकला दृष्टिकोण के साथ समस्या यह है कि गतिविधियां और टुकड़े काफी बड़े और परीक्षण के लिए बहुत कठिन हो सकते हैं।
एक और दृष्टिकोण जो अधिक तार्किक (और सही) लगता है वह है जहां एमवीसी दुनिया में गतिविधियां और टुकड़े दृश्य होना चाहिए। नियंत्रक अलग वर्ग होने चाहिए जो किसी भी Android वर्ग का विस्तार या उपयोग नहीं करते हैं। मॉडल के लिए भी यही।
वैसे भी यदि आप एमवीसी के बारे में अधिक जांच करते हैं, तो आप पाएंगे कि एंड्रॉइड प्रोजेक्ट पर लागू होने पर, कोड परतें सही तरीके से भी एक-दूसरे पर निर्भर करती हैं। इसलिए मैं यह अनुशंसा नहीं करूंगा कि आप इसे अब अपने अगले Android ऐप के लिए उपयोग करें।
मॉडल-व्यू-प्रेजेंटर (एमवीपी) पैटर्न
पहले दृष्टिकोण के बाद, जो काम नहीं करता था, एंड्रॉइड डेवलपर्स आगे बढ़े और सबसे लोकप्रिय वास्तुशिल्प पैटर्न - एमवीपी में से एक का उपयोग करने का प्रयास किया। यह पैटर्न आर्किटेक्चर पसंद के दूसरे पुनरावृत्ति का प्रतिनिधित्व करता है। यह पैटर्न व्यापक रूप से उपयोग किया जाता है और अभी भी एक अनुशंसित है। जो कोई भी Android विकास शुरू करता है, उसके लिए सीखना आसान है। आइए इसकी 3 अलग-अलग परतों वाली भूमिकाओं पर एक नज़र डालें:
मॉडल - डेटा परत, जो एमवीसी पैटर्न के समान है। नेटवर्क और डेटाबेस परतों के साथ व्यावसायिक तर्क और संचार को संभालने के लिए जिम्मेदार।
देखें - यूजर इंटरफेस (यूआई) परत। डेटा प्रदर्शित करता है और प्रस्तुतकर्ता को उपयोगकर्ता क्रियाओं के बारे में सूचित करता है।
प्रस्तुतकर्ता - मॉडल से डेटा पुनर्प्राप्त करता है, UI तर्क लागू करता है और दृश्य की स्थिति का प्रबंधन करता है, यह तय करता है कि क्या प्रदर्शित करना है, और दृश्य से उपयोगकर्ता इनपुट सूचनाओं पर प्रतिक्रिया करता है। यह अनिवार्य रूप से MVC से नियंत्रक है, सिवाय इसके कि यह दृश्य से बिल्कुल भी बंधा नहीं है, केवल एक इंटरफ़ेस है।
एमवीपी स्कीमा से पता चलता है कि दृश्य और प्रस्तुतकर्ता निकट से संबंधित हैं। उन्हें एक दूसरे के संदर्भ में होना चाहिए। उनका संबंध Contract
. में परिभाषित किया गया है इंटरफ़ेस वर्ग।
इस पैटर्न में एक महत्वपूर्ण लेकिन नियंत्रणीय नुकसान है। यदि आप पर्याप्त रूप से सावधान नहीं हैं और एकल जिम्मेदारी सिद्धांत के अनुसार अपना कोड नहीं तोड़ते हैं तो प्रस्तुतकर्ता एक विशाल सर्वज्ञ वर्ग में विस्तार करता है। हालांकि, आम तौर पर बोलते हुए, एमवीपी पैटर्न चिंताओं का एक बहुत अच्छा अलगाव प्रदान करता है। यह आपके प्रोजेक्ट के लिए आपकी मुख्य पसंद हो सकती है।
मॉडल-व्यू-व्यूमॉडल (MVVM) पैटर्न
एमवीवीएम पैटर्न दृष्टिकोण का तीसरा पुनरावृत्ति है। यह एंड्रॉइड आर्किटेक्चर कंपोनेंट्स रिलीज के साथ एंड्रॉइड टीम द्वारा अनुशंसित आर्किटेक्चर पैटर्न बन गया। इसलिए हम सबसे ज्यादा इस पैटर्न को सीखने पर ध्यान देंगे। इसके अलावा मैं इसे "माई क्रिप्टो कॉइन्स" ऐप के लिए इस्तेमाल करूंगा। पहले की तरह, आइए इसकी अलग कोड परतों पर एक नज़र डालें:
मॉडल - डेटा स्रोत का सार। ViewModel डेटा प्राप्त करने और सहेजने के लिए मॉडल के साथ काम करता है।
देखें — जो ViewModel को उपयोगकर्ताओं के कार्यों के बारे में सूचित करता है।
ViewModel — दृश्य के लिए प्रासंगिक डेटा की धाराओं को उजागर करता है।
एमवीपी पैटर्न की तुलना में अंतर यह है कि, एमवीवीएम में, व्यूमोडेल में व्यू का संदर्भ नहीं है क्योंकि यह प्रस्तुतकर्ता के साथ है। एमवीवीएम में, व्यूमोडेल घटनाओं की एक धारा का खुलासा करता है जिससे विभिन्न दृश्य जुड़ सकते हैं। दूसरी ओर, एमवीपी मामले में, प्रस्तुतकर्ता सीधे दृश्य को बताता है कि क्या प्रदर्शित करना है। आइए एमवीवीएम स्कीमा पर एक नजर डालते हैं:
एमवीवीएम में व्यू में व्यूमोडेल का संदर्भ है। व्यूमोडेल को व्यू के बारे में कोई जानकारी नहीं है। व्यू और व्यूमॉडल के बीच कई-से-एक संबंध हैं।
एमवीसी बनाम एमवीपी बनाम एमवीवीएम की तुलना
यहाँ एक तालिका है जो मेरे द्वारा बताए गए सभी पैटर्न का सार प्रस्तुत करती है:
जैसा कि आपने देखा होगा, मॉड्यूलर और परीक्षण योग्य आधुनिक ऐप बनाते समय एमवीपी और एमवीवीएम की तुलना में एमवीसी इतना अच्छा नहीं है। लेकिन प्रत्येक पैटर्न के अपने फायदे और नुकसान हैं। यह एक अच्छा विकल्प है यदि यह आपकी आवश्यकताओं के अनुरूप है। मेरा सुझाव है कि आप जांच करें और इन सभी पैटर्न के बारे में अधिक जानें क्योंकि यह इसके लायक है।
इस बीच, मैं 2018 में ट्रेंडिंग पैटर्न के साथ अपना प्रोजेक्ट जारी रखूंगा, जिसे Google - MVVM द्वारा भी आगे बढ़ाया गया है।
एंड्रॉइड आर्किटेक्चर घटक
यदि आप Android एप्लिकेशन जीवनचक्र से परिचित हैं, तो आप जानते हैं कि एक ऐसा ऐप बनाना कितना सिरदर्द हो सकता है जो सभी डेटा प्रवाह समस्याओं और दृढ़ता और स्थिरता के मुद्दों से बचा जाता है जो आमतौर पर कॉन्फ़िगरेशन परिवर्तन के दौरान दिखाई देते हैं।
2017 में, Android टीम ने फैसला किया कि हम काफी संघर्ष करेंगे। उन्होंने जिम्मेदारी संभाली और एंड्रॉइड आर्किटेक्चर कंपोनेंट्स फ्रेमवर्क पेश किया। यह अंततः आपको अपने कोड को जटिल किए बिना या यहां तक कि हैक लागू किए बिना इन सभी मुद्दों को हल करने देता है।
एंड्रॉइड आर्किटेक्चर कंपोनेंट्स पुस्तकालयों का एक संग्रह है जो आपको मजबूत, परीक्षण योग्य और रखरखाव योग्य ऐप डिज़ाइन करने में मदद करता है। वर्तमान समय में जब मैं यह ब्लॉग पोस्ट लिख रहा हूँ, इसमें ये घटक शामिल हैं:
- डेटा बाइंडिंग — घोषित रूप से देखने योग्य डेटा को UI तत्वों से बांधता है
- जीवनचक्र — अपनी गतिविधि और खंड जीवनचक्र प्रबंधित करें
- LiveData — अंतर्निहित डेटाबेस में परिवर्तन होने पर दृश्यों को सूचित करें
- नेविगेशन — इन-ऐप नेविगेशन के लिए आवश्यक सभी चीज़ों को संभालें
- पेजिंग — अपने डेटा स्रोत से मांग पर जानकारी को धीरे-धीरे लोड करें
- कक्ष — धाराप्रवाह SQLite डेटाबेस एक्सेस
- ViewModel — जीवनचक्र-सचेत तरीके से UI-संबंधित डेटा प्रबंधित करें
- कार्य प्रबंधक — अपने Android पृष्ठभूमि कार्य प्रबंधित करें
एंड्रॉइड आर्किटेक्चर कंपोनेंट्स की मदद से हम इस डायग्राम के बाद माई क्रिप्टो कॉइन्स ऐप में एमवीवीएम आर्किटेक्चर पैटर्न को लागू करने जा रहे हैं:
यह Google द्वारा अनुशंसित आर्किटेक्चर है। यह दिखाता है कि सभी मॉड्यूल को एक दूसरे के साथ कैसे इंटरैक्ट करना चाहिए। आगे हम केवल विशिष्ट Android आर्किटेक्चर घटकों को कवर करेंगे जिनका उपयोग हम अपने प्रोजेक्ट में करेंगे।
अपनी स्रोत फ़ाइलों को व्यवस्थित करना
विकास शुरू करने से पहले हमें इस बात पर विचार करना चाहिए कि हमें अपनी परियोजना की स्रोत फाइलों को कैसे व्यवस्थित करना चाहिए। हम इस प्रश्न को अनुत्तरित नहीं छोड़ सकते, क्योंकि बाद में हमारे पास एक गड़बड़ संरचना होगी जिसे समझना और संशोधित करना कठिन होगा।
इसे करने के कई तरीके हैं। एक घटक श्रेणी द्वारा व्यवस्थित करना है। उदाहरण के लिए, सभी गतिविधियां अपने स्वयं के फ़ोल्डर में जाती हैं, सभी एडेप्टर अपने फ़ोल्डर में जाते हैं और इसी तरह।
दूसरा तरीका यह होगा कि सब कुछ ऐप सुविधाओं के आधार पर व्यवस्थित किया जाए। उदाहरण के लिए, सभी क्रिप्टो मुद्राओं में क्रिप्टो खोजें और जोड़ें सूची सुविधा अपने addsearchlist
. पर जाती है फ़ोल्डर। मुख्य विचार यह है कि सब कुछ बेतरतीब ढंग से रखने के बजाय आपको इसे किसी विशिष्ट तरीके से करने की आवश्यकता है। मैं इन दोनों के किसी प्रकार के मिश्रण का उपयोग करता हूं।
प्रोजेक्ट की फ़ोल्डर संरचना के अलावा, आपको प्रोजेक्ट फ़ाइलों के नामकरण के लिए कुछ नियम लागू करने पर विचार करना चाहिए। उदाहरण के लिए, Android कक्षाओं का नामकरण करते समय आपको कक्षा के उद्देश्य को नाम में स्पष्ट रूप से परिभाषित करना चाहिए।
व्यूमॉडल
हमारे ऐप आर्किटेक्चर विकास की शुरुआत के लिए, सबसे पहले हम ViewModel बनाने जा रहे हैं। व्यू मॉडल वे ऑब्जेक्ट होते हैं जो UI घटकों के लिए डेटा प्रदान करते हैं और कॉन्फ़िगरेशन परिवर्तनों से बचे रहते हैं।
आप किसी गतिविधि या खंड के पूरे जीवनचक्र में डेटा बनाए रखने के लिए ViewModel का उपयोग कर सकते हैं। क्रियाएँ और अंश अल्पकालिक वस्तुएँ हैं। जैसे ही कोई उपयोगकर्ता किसी ऐप के साथ इंटरैक्ट करता है, वे बार-बार बनाए और नष्ट किए जाते हैं। एक ViewModel नेटवर्क संचार से संबंधित कार्यों के प्रबंधन के साथ-साथ डेटा हेरफेर और दृढ़ता के लिए भी बेहतर अनुकूल है।
उदाहरण के तौर पर अब MainListFragment
. के लिए एक ViewModel बनाते हैं UI डेटा को इससे अलग करने के लिए।
class MainViewModel : ViewModel() {
...
}
फिर कोड की एकल पंक्ति के साथ ViewModel प्राप्त करें।
class MainListFragment : Fragment() {
...
private lateinit var viewModel: MainViewModel
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupList()
// Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
...
}
...
}
मूल रूप से यही है, बधाई! ? चलिए आगे बढ़ते हैं।
लाइवडेटा
LiveData एक अवलोकन योग्य डेटा धारक वर्ग है। यह पर्यवेक्षक पैटर्न का अनुसरण करता है। LiveData जीवनचक्र-जागरूक है। इसका मतलब है कि यह केवल ऐप घटक (गतिविधि, टुकड़ा, आदि) पर्यवेक्षकों को अपडेट करता है जो एक सक्रिय जीवनचक्र स्थिति में हैं।
LiveData वर्ग डेटा का नवीनतम मान लौटाता है। जब डेटा बदलता है तो यह अद्यतन मान लौटाता है। LiveData ViewModel के लिए सबसे उपयुक्त है।
हम इस तरह ViewModel के साथ LiveData का उपयोग करेंगे:
...
class MainViewModel : ViewModel() {
private val liveData = MutableLiveData<ArrayList<Cryptocurrency>>()
val data: LiveData<ArrayList<Cryptocurrency>>
get() = liveData
init {
val tempData = ArrayList<Cryptocurrency>()
val btc:Cryptocurrency = Cryptocurrency("Bitcoin", 1, 0.56822348, "BTC", 8328.77, 4732.60, 0.19, -10.60, 0.44, 20.82)
val eth:Cryptocurrency = Cryptocurrency("Etherium", 2, 6.0, "ETH", 702.99, 4217.94, 0.13, -7.38, 0.79, 33.32)
tempData.add(btc)
tempData.add(eth)
liveData.value = tempData
}
}
LiveData के रूप में प्रदर्शित ViewModel पर डेटा देखें:
...
class MainListFragment : Fragment() {
private lateinit var recyclerView: RecyclerView
private lateinit var recyclerAdapter: MainRecyclerViewAdapter
private lateinit var viewModel: MainViewModel
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupList()
// Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// Observe data on the ViewModel, exposed as a LiveData
viewModel.data.observe(this, Observer { data ->
// Set the data exposed by the LiveData
if (data != null) {
recyclerAdapter.setData(data)
}
})
}
...
}
यहां इतिहास में इस बिंदु पर भंडार ब्राउज़ करें।
डेटा बाइंडिंग
XML लेआउट से कनेक्ट करने के लिए आवश्यक बॉयलरप्लेट कोड को हटाने के लिए डेटा बाइंडिंग लाइब्रेरी बनाई गई थी।
अपने कोटलिन प्रोजेक्ट्स में डेटा बाइंडिंग का उपयोग करने के लिए, आपको केप्ट कंपाइलर प्लगइन के साथ एनोटेशन प्रोसेसर के लिए समर्थन चालू करना होगा। Android कॉन्फ़िगरेशन gradle फ़ाइल में डेटा बाइंडिंग ब्लॉक भी जोड़ें:
...
apply plugin: 'kotlin-kapt'
android {
...
dataBinding {
enabled = true
}
}
...
डेटा बाइंडिंग जेनरेटेड क्लासेस का उपयोग करने के लिए, हमें सभी व्यू कोड को <layo
. में डालना होगा यूटी> टैग। डेटा बाइंडिंग की सबसे शक्तिशाली अवधारणा यह है कि हम कुछ डेटा क्लास को xml लेआउट और आइटम प्रॉपर्टीज़ को सीधे फ़ील्ड में बाँध सकते हैं।
<layout xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools">
<data>
<variable
name="cryptocurrency"
type="com.baruckis.mycryptocoins.data.Cryptocurrency" />
</data>
...
<android.support.v7.widget.AppCompatTextView
android:id="@+id/item_name"
style="@style/MainListItemPrimeText"
android:layout_marginEnd="@dimen/main_cardview_list_item_text_between_margin"
android:layout_marginStart="@dimen/main_cardview_list_item_inner_margin"
android:text="@{cryptocurrency.name}"
android:textAlignment="viewStart"
app:layout_constraintBottom_toTopOf="@+id/item_amount_symbol"
app:layout_constraintEnd_toStartOf="@+id/guideline1_percent"
app:layout_constraintStart_toEndOf="@+id/item_image_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="spread"
tools:text="@string/sample_text_item_name" />
...
</layout>
डेटा बिडिंग के साथ RecyclerView अडैप्टर इस तरह दिखेगा:
class MainRecyclerViewAdapter() : RecyclerView.Adapter<MainRecyclerViewAdapter.BindingViewHolder>() {
private lateinit var dataList: ArrayList<Cryptocurrency>
fun setData(newDataList: ArrayList<Cryptocurrency>) {
dataList = newDataList
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = FragmentMainListItemBinding.inflate(inflater, parent, false)
return BindingViewHolder(binding)
}
override fun onBindViewHolder(holder: BindingViewHolder, position: Int) = holder.bind(dataList[position])
override fun getItemCount(): Int = dataList.size
...
inner class BindingViewHolder(var binding: FragmentMainListItemBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(cryptocurrency: Cryptocurrency) {
binding.cryptocurrency = cryptocurrency
binding.itemRanking.text = String.format("${cryptocurrency.rank}")
...
binding.executePendingBindings()
}
}
}
अंत में कोई और लेखन नहीं findViewById
? यहां इतिहास में इस बिंदु पर भंडार ब्राउज़ करें।
कक्ष
हमारे ऐप को उपयोगकर्ता के पास मौजूद विभिन्न क्रिप्टोकरेंसी के लगातार डेटा को स्टोर करने की आवश्यकता है। इसे स्थानीय डेटाबेस के अंदर संग्रहित किया जाना चाहिए जिसे निजी तौर पर Android डिवाइस के अंदर रखा जाता है।
एक निजी डेटाबेस में संरचित डेटा संग्रहीत करने के लिए हम SQLite डेटाबेस का उपयोग करेंगे। यह अक्सर सबसे अच्छा विकल्प होता है।
हमारे ऐप के लिए SQLite डेटाबेस बनाने के लिए हम रूम का उपयोग करेंगे। कक्ष Android टीम द्वारा बनाई गई एक दृढ़ता पुस्तकालय है जो SQLite के ऊपर एक आवरण है। यह एक अमूर्त परत है जो SQLite के साथ इंटरैक्ट करने के लिए आवश्यक अधिकांश बॉयलरप्लेट कोड को हटा देती है। यह आपके SQL प्रश्नों की संकलन-समय जाँच भी जोड़ता है।
इसके बारे में सोचने का सबसे अच्छा तरीका एक ORM (ऑब्जेक्ट रिलेशनल मैपर) टूल है जिसे आपके डेटाबेस में आपके ऑब्जेक्ट इंस्टेंस और पंक्तियों के बीच मैप करने के लिए स्वचालित रूप से ग्लू कोड जेनरेट करने के लिए डिज़ाइन किया गया है।
रूम में मूल रूप से 3 प्रमुख घटक होते हैं:
- इकाई - यह घटक उस वर्ग का प्रतिनिधित्व करता है जिसमें डेटाबेस पंक्ति होती है। प्रत्येक इकाई के लिए, आइटम रखने के लिए एक डेटाबेस तालिका बनाई जाती है।
- DAO (डेटा एक्सेस ऑब्जेक्ट) — मुख्य घटक जो डेटाबेस तक पहुंचने के तरीकों को परिभाषित करने के लिए जिम्मेदार है।
- डेटाबेस - एक घटक जो धारक वर्ग है जो संस्थाओं की सूची, डीएओ की सूची और डेटाबेस संस्करण को परिभाषित करने के लिए एनोटेशन का उपयोग करता है और अंतर्निहित कनेक्शन के लिए मुख्य पहुंच बिंदु के रूप में कार्य करता है।
आइए हमारे My Crypto Coins ऐप में रूम सेटअप करने के लिए इन आसान चरणों का पालन करें:
- एक इकाई बनाएं।
@Entity
data class Cryptocurrency(val name: String,
val rank: Short,
val amount: Double,
@PrimaryKey
val symbol: String,
val price: Double,
val amountFiat: Double,
val pricePercentChange1h: Double,
val pricePercentChange7d: Double,
val pricePercentChange24h: Double,
val amountFiatChange24h: Double)
कक्ष को डेटाबेस में इसकी संरचना के बारे में बताने के लिए कुछ अतिरिक्त जानकारी जोड़ें।
2. डीएओ बनाएं।
@Dao
interface MyCryptocurrencyDao {
@Query("SELECT * FROM Cryptocurrency")
fun getMyCryptocurrencyLiveDataList(): LiveData<List<Cryptocurrency>>
@Insert
fun insertDataToMyCryptocurrencyList(data: List<Cryptocurrency>)
}
शुरुआत के लिए, हम एक डीएओ बनाने जा रहे हैं जो हमें केवल उस तालिका से रिकॉर्ड प्राप्त करने की अनुमति देता है जिसे हमने इकाई के साथ बनाया है और कुछ नमूना डेटा भी सम्मिलित करने के लिए।
3. डेटाबेस बनाएं और सेटअप करें।
यह कहना महत्वपूर्ण है कि डेटाबेस उदाहरण आदर्श रूप से प्रति सत्र केवल एक बार बनाया जाना चाहिए। इसे प्राप्त करने का एक तरीका सिंगलटन पैटर्न का उपयोग करना होगा।
@Database(entities = [Cryptocurrency::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun myCryptocurrencyDao(): MyCryptocurrencyDao
// The AppDatabase a singleton to prevent having multiple instances of the database opened at the same time.
companion object {
// Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.
@Volatile
private var instance: AppDatabase? = null
// For Singleton instantiation.
fun getInstance(context: Context): AppDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
// Creates and pre-populates the database.
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
// Prepopulate the database after onCreate was called.
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// Insert the data on the IO Thread.
ioThread {
getInstance(context).myCryptocurrencyDao().insertDataToMyCryptocurrencyList(PREPOPULATE_DATA)
}
}
})
.build()
}
// Sample data.
val btc: Cryptocurrency = Cryptocurrency("Bitcoin", 1, 0.56822348, "BTC", 8328.77, 4732.60, 0.19, -10.60, 0.44, 20.82)
val eth: Cryptocurrency = Cryptocurrency("Etherium", 2, 6.0, "ETH", 702.99, 4217.94, 0.13, -7.38, 0.79, 33.32)
val PREPOPULATE_DATA = listOf(btc, eth)
}
}
private val IO_EXECUTOR = Executors.newSingleThreadExecutor()
// Utility method to run blocks on a dedicated background thread, used for io/database work.
fun ioThread(f : () -> Unit) {
IO_EXECUTOR.execute(f)
}
जैसा कि आप प्रारंभिक रन पर देखते हैं, डेटाबेस केवल परीक्षण उद्देश्यों के लिए कुछ नमूना डेटा के साथ पहले से तैयार हो जाएगा।
4. अतिरिक्त चरण। रिपोजिटरी बनाएं।
रिपोजिटरी आर्किटेक्चर कंपोनेंट्स लाइब्रेरी का हिस्सा नहीं है। यह कोड पृथक्करण और वास्तुकला के लिए सुझाया गया सर्वोत्तम अभ्यास है।
यदि आपको एकाधिक डेटा स्रोतों को प्रबंधित करना है तो यह सभी ऐप डेटा के लिए सत्य के एकल स्रोत के रूप में खड़ा है।
class MyCryptocurrencyRepository private constructor(
private val myCryptocurrencyDao: MyCryptocurrencyDao
) {
fun getMyCryptocurrencyLiveDataList(): LiveData<List<Cryptocurrency>> {
return myCryptocurrencyDao.getMyCryptocurrencyLiveDataList()
}
companion object {
// Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.
@Volatile
private var instance: MyCryptocurrencyRepository? = null
// For Singleton instantiation.
fun getInstance(myCryptocurrencyDao: MyCryptocurrencyDao) =
instance ?: synchronized(this) {
instance
?: MyCryptocurrencyRepository(myCryptocurrencyDao).also { instance = it }
}
}
}
हम इस रिपॉजिटरी का उपयोग अपने ViewModel में करने जा रहे हैं।
class MainViewModel(myCryptocurrencyRepository: MyCryptocurrencyRepository) : ViewModel() {
val liveData = myCryptocurrencyRepository.getMyCryptocurrencyLiveDataList()
}
हमारा फ्रैगमेंट कोड भी विकसित होता है।
class MainListFragment : Fragment() {
...
private lateinit var viewModel: MainViewModel
...
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setupList()
subscribeUi()
}
...
private fun subscribeUi() {
val factory = InjectorUtils.provideMainViewModelFactory(requireContext())
// Obtain ViewModel from ViewModelProviders, using this fragment as LifecycleOwner.
viewModel = ViewModelProviders.of(this, factory).get(MainViewModel::class.java)
// Update the list when the data changes by observing data on the ViewModel, exposed as a LiveData.
viewModel.liveData.observe(this, Observer<List<Cryptocurrency>> { data ->
if (data != null && data.isNotEmpty()) {
emptyListView.visibility = View.GONE
recyclerView.visibility = View.VISIBLE
recyclerAdapter.setData(data)
} else {
recyclerView.visibility = View.GONE
emptyListView.visibility = View.VISIBLE
}
})
}
}
क्योंकि हमारे ViewModel वर्ग में अब एक कंस्ट्रक्टर है जो अब खाली नहीं है, हमें एक प्रदाता फ़ैक्टरी पैटर्न को लागू करने की आवश्यकता है। इसे ViewModelProviders.of()
. को पास कर दिया जाएगा दूसरे पैरामीटर के रूप में विधि।
object InjectorUtils {
fun provideMainViewModelFactory(
context: Context
): MainViewModelFactory {
val repository = getMyCryptocurrencyRepository(context)
return MainViewModelFactory(repository)
}
private fun getMyCryptocurrencyRepository(context: Context): MyCryptocurrencyRepository {
return MyCryptocurrencyRepository.getInstance(
AppDatabase.getInstance(context).myCryptocurrencyDao())
}
}
class MainViewModelFactory(private val repository: MyCryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return MainViewModel(repository) as T
}
}
यहां इतिहास में इस बिंदु पर भंडार ब्राउज़ करें।
अंतिम विचार
डिज़ाइन आर्किटेक्चर, जिसकी हमने इस भाग में चर्चा की थी, को सूचित दिशानिर्देशों के रूप में उपयोग किया जाना चाहिए, लेकिन कठोर नियम नहीं। मैं प्रत्येक विषय पर बहुत अधिक विस्तार में नहीं जाना चाहता था। Android आर्किटेक्चर कंपोनेंट्स के साथ हमने कोडिंग प्रक्रिया पर एक नज़र डाली। ध्यान रखें कि प्रत्येक घटक पर व्यक्तिगत रूप से सीखने के लिए बहुत कुछ है और मैं आपको ऐसा करने की सलाह देता हूं।
आइए उन सभी चीजों को संक्षेप में प्रस्तुत करें जिन्हें हम पहले से ही बनाने का प्रबंधन करते हैं:
- माई क्रिप्टो कॉइन्स ऐप में, प्रत्येक अलग स्क्रीन का अपना व्यूमॉडल होता है। यह किसी भी कॉन्फ़िगरेशन परिवर्तन से बचेगा और उपयोगकर्ता को किसी भी डेटा हानि से बचाएगा।
- ऐप का यूजर इंटरफेस एक प्रतिक्रियाशील प्रकार है। इसका मतलब है कि बैक-एंड में डेटा बदलने पर यह तुरंत अपडेट हो जाएगा। यह LiveData की मदद से किया जाता है।
- हमारे प्रोजेक्ट में कोड कम है क्योंकि हम डेटा बाइंडिंग का उपयोग करके सीधे अपने कोड में वेरिएबल से जुड़ते हैं।
- आखिरकार, हमारा ऐप उपयोगकर्ता डेटा को स्थानीय रूप से डिवाइस के अंदर SQLite डेटाबेस के रूप में संग्रहीत करता है। डेटाबेस आसानी से कक्ष घटक के साथ बनाया गया था। ऐप का कोड सुविधाओं द्वारा संरचित है और सभी प्रोजेक्ट आर्किटेक्चर एमवीवीएम है - एंड्रॉइड टीम द्वारा अनुशंसित पैटर्न।
रिपॉजिटरी
अब, जैसा कि आप "क्रिप्टोफोलियो" (पहले "माई क्रिप्टो कॉइन्स") ऐप देख रहे हैं, वास्तव में आकार लेना शुरू कर रहा है। इस भाग 3 के लिए नवीनतम रिपोजिटरी प्रतिबद्धता के साथ, आप इसे उपयोगकर्ता के लिए पहले से भरे हुए डेटाबेस डेटा को अच्छी तरह से दिखाते हुए पा सकते हैं, जिसमें कुल होल्डिंग पोर्टफोलियो मूल्य की सही गणना की गई है।
GitHub पर स्रोत देखें
आशी! पढ़ने के लिए धन्यवाद! मैंने मूल रूप से इस पोस्ट को 22 अगस्त, 2018 को अपने निजी ब्लॉग www.baruckis.com के लिए प्रकाशित किया था।