बैठक कक्ष L̶i Roomt̶t̶l̶eper हेल्पर v 2

यह आलेख मीटिंग रूम हेल्पर मोबाइल एप्लिकेशन के विकास के चरणों का विस्तार से वर्णन करता है: विचार की शुरुआत से लेकर रिलीज़ होने तक। आवेदन कोटलिन में लिखा गया है और डेटा बाइंडिंग का उपयोग किए बिना एक सरल MVVM वास्तुकला पर बनाया गया है। UI भाग LiveData ऑब्जेक्ट का उपयोग करके अपडेट किया गया है। डेटा बाइंडिंग से इनकार करने के कारणों को विस्तृत और समझाया गया है। वास्तुकला कई दिलचस्प समाधानों का उपयोग करता है जो प्रोग्राम को तार्किक रूप से छोटी फ़ाइलों में विभाजित करना संभव बनाता है, जो अंततः कोड समर्थन को सरल करता है।



परियोजना विवरण


3 साल पहले, हमारी कंपनी बैठक के कमरे की तत्काल बुकिंग के लिए एक छोटी परियोजना विकसित करने का विचार लेकर आई थी। अधिकांश एचआर प्रबंधक और अर्काडिया ऐसे उद्देश्यों के लिए आउटलुक कैलेंडर का उपयोग करना पसंद करते हैं, लेकिन बाकी के बारे में क्या?

मैं डेवलपर के जीवन से 2 उदाहरण दूंगा

  1. किसी भी टीम को समय-समय पर 5-10 मिनट के लिए एक त्वरित रैली आयोजित करने की सहज इच्छा होती है। यह इच्छा कार्यालय के किसी भी कोने में डेवलपर्स से आगे निकल सकती है, और इसलिए अपने आस-पास के सहयोगियों को विचलित नहीं करने के लिए, वे (डेवलपर्स और न केवल) एक मुक्त बातचीत के लिए देखना शुरू करते हैं। कमरे से कमरे में पलायन (हमारे कार्यालय में बैठक के कमरे एक पंक्ति में व्यवस्थित होते हैं), सहकर्मी "ध्यान से जांचें" कि कौन से कमरे वर्तमान में मुफ्त हैं। नतीजतन, वे अंदर के सहयोगियों को विचलित करते हैं। इस तरह के लोग हमेशा से रहे हैं और हमेशा रहेंगे, भले ही रैली के व्यवधान के लिए कॉर्पोरेट चार्टर में गोली मार दी जाए। जिसने समझा, वह समझेगा।
  2. और यहाँ एक और मामला है। आपने अभी-अभी भोजन कक्ष छोड़ा है और अपने आप को अग्रसर कर रहे हैं, लेकिन यहां आपके सहकर्मी (या प्रबंधक) दूसरे विभाग से आपको स्वीकार करते हैं। वह आपको कुछ जरूरी बताना चाहता है, और इन उद्देश्यों के लिए आपको एक बैठक कक्ष की आवश्यकता है। नियमों के अनुसार, आपको पहले एक कमरा (अपने फोन या कंप्यूटर से) बुक करना होगा और उसके बाद ही उस पर कब्जा करना होगा। यदि आपके पास मोबाइल आउटलुक वाला मोबाइल फोन है तो अच्छा है। और अगर नहीं? कंप्यूटर पर वापस जाएं, फिर बैठक कक्ष में वापस जाएं? प्रत्येक कर्मचारी को फोन पर आउटलुक एक्सप्रेस रखने के लिए मजबूर करने और यह सुनिश्चित करने के लिए कि हर कोई उनके साथ फोन करता है? ये हमारे तरीके नहीं हैं।

यही कारण है कि 2.5 साल पहले बैठक कक्ष में से प्रत्येक अपने स्वयं के टैबलेट से सुसज्जित था:



इस परियोजना के लिए, मेरे सहयोगी ने आवेदन का पहला संस्करण विकसित किया: मीटिंग रूम लिटिल हेल्पर ( यहां आप इसके बारे में पढ़ सकते हैं )। MRLH ने आरक्षण बुक करने, आरक्षण रद्द करने और नवीनीकरण करने की अनुमति दी, शेष वार्तालापों की स्थिति दिखाई दी। एक कर्मचारी की पहचान (Microsoft फेस एपीआई क्लाउड सेवा और हमारे आंतरिक विश्लेषक का उपयोग करके) को पहचानना एक अभिनव "चाल" बन गया है। आवेदन ठोस निकला और कंपनी को 2.5 साल तक ईमानदारी से काम किया।

लेकिन समय बीतता गया ... नए विचार प्रकट हुए। मैं कुछ नया करना चाहता था, और इसलिए हमने आवेदन को फिर से लिखने का फैसला किया।

संदर्भ की शर्तें


जैसा कि अक्सर होता है - लेकिन, दुर्भाग्य से, हमेशा नहीं - तकनीकी विनिर्देशों की तैयारी के साथ विकास शुरू हुआ। सबसे पहले, हमने उन लोगों को बुलाया, जो अक्सर आरक्षण के लिए गोलियों का उपयोग करते हैं। यह सिर्फ इतना हुआ कि उन सभी को एचआर और प्रबंधकों की लत लग गई जो पहले आउटलुक का विशेष रूप से उपयोग करते थे। उनसे हमें निम्न प्रतिक्रिया मिली (आवश्यकताओं से यह तुरंत स्पष्ट हो जाता है कि एचआर ने क्या मांगा और प्रबंधकों ने क्या मांगा):

  • आपको किसी भी टैबलेट से किसी भी मीटिंग रूम को बुक करने की क्षमता को जोड़ना होगा (पहले, प्रत्येक टैबलेट ने आपको केवल अपने कमरे को बुक करने की अनुमति दी थी);
  • पूरे दिन की बैठक (आदर्श रूप से, किसी भी दिन) के लिए रैलियों के कार्यक्रम को देखना अच्छा होगा;
  • संपूर्ण विकास चक्र को थोड़े समय के लिए (6-7 सप्ताह के लिए) किया जाना चाहिए।

ग्राहक की इच्छाओं के साथ सब कुछ स्पष्ट है, लेकिन तकनीकी आवश्यकताओं और भविष्य के बारे में क्या? डेवलपर्स गिल्ड से परियोजना के लिए कुछ आवश्यकताओं को जोड़ें:

  • सिस्टम को मौजूदा गोलियों के साथ और नए लोगों के साथ काम करना चाहिए;
  • सिस्टम की स्केलेबिलिटी - 50 बातचीत और ऊपर से (यह अधिकांश ग्राहकों के लिए मार्जिन के साथ पर्याप्त होना चाहिए अगर सिस्टम को दोहराने के लिए शुरू होता है);
  • पिछली कार्यक्षमता बनाए रखना (एप्लिकेशन के पहले संस्करण ने Outlook सेवाओं के साथ संचार करने के लिए जावा एपीआई का उपयोग किया था, और हमने इसे एक विशेष Microsoft ग्राफ़ एपीआई के साथ बदलने की योजना बनाई थी, इसलिए यह कार्यक्षमता खोना महत्वपूर्ण नहीं था);
  • ऊर्जा की खपत को कम करना (गोलियाँ एक बाहरी बैटरी द्वारा संचालित होती हैं, क्योंकि व्यावसायिक केंद्र ने इसकी दीवारों को हमारे तारों को बिछाने के लिए ड्रिलिंग की अनुमति नहीं दी थी);
  • नई UX / UI डिज़ाइन, सभी नवाचारों को प्रतिबिंबित करती है।

कुल 8 अंक। आवश्यकताएँ काफी निष्पक्ष हैं। इसके अतिरिक्त, हम सामान्य विकास नियमों को निर्धारित करते हैं:

  • केवल उन्नत तकनीकों का उपयोग करें (यह टीम को विशेषज्ञों के रूप में विकसित करने और एक जगह पर स्थिर न होने की अनुमति देगा, जबकि भविष्य के भविष्य में परियोजना के समर्थन को सरल बनाते हुए);
  • सर्वोत्तम प्रथाओं का पालन करें, लेकिन उन्हें आँख बंद करके न लें किसी भी पेशेवर का मुख्य नियम (और इसके लिए प्रयास करने वाला डेवलपर) सब कुछ का आलोचनात्मक मूल्यांकन करना है;
  • स्वच्छ और सुव्यवस्थित कोड लिखना (शायद यह सबसे कठिन है जब आप नवाचार और तंग विकास समय को संयोजित करने की कोशिश कर रहे हैं)।

एक शुरुआत की गई है। यह, हमेशा की तरह, उत्साही है! देखते हैं आगे क्या होता है।

डिज़ाइन


UX डिजाइनर द्वारा विकसित एप्लीकेशन डिजाइन:




यह मुख्य स्क्रीन है। इसे अधिकांश समय प्रदर्शित किया जाएगा। सभी आवश्यक जानकारी ergonomically यहाँ स्थित है:

  • कमरे का नाम और उसकी संख्या;
  • वर्तमान स्थिति;
  • अगली बैठक तक (या इसके अंत तक) समय;
  • स्क्रीन के तल पर शेष कमरों की स्थिति।

कृपया ध्यान दें: डायल केवल 12 घंटे प्रदर्शित करता है, जैसा कि सिस्टम कंपनी की जरूरतों के लिए कॉन्फ़िगर किया गया है (आर्काडिया टैबलेट्स सुबह 8 बजे से रात 8 बजे तक काम करती हैं, स्वचालित रूप से चालू और बंद होती हैं)




एक कमरा बुक करने के लिए, बस बुकिंग विंडो को कॉल करें और रैली की अवधि का संकेत दें। शेष कमरों की बुकिंग के चरण समान हैं, वे केवल कमरे के आइकन पर क्लिक करके शुरू करते हैं।



यदि आप किसी विशिष्ट समय के लिए मीटिंग शेड्यूल करना चाहते हैं, तो मीटिंग टैब की मीटिंग की सूची में, अगले टैब पर जाएं, और खाली समय पर क्लिक करें। इसके अलावा, पहले मामले में सब कुछ जैसा है।

पूर्ण संक्रमण वृक्ष कुछ इस तरह दिखना चाहिए:




आइए इसे सक्षम रूप से लागू करने का प्रयास करें।

प्रौद्योगिकी ढेर


विकास तकनीकें तेजी से विकसित हो रही हैं और बदल रही हैं। एक और 2 वर्षों के लिए, जावा आधिकारिक Android विकास भाषा थी। सभी ने जावा में लिखा और डेटा बाइंडिंग का इस्तेमाल किया। अब, यह मुझे लगता है, हम प्रतिक्रियाशील प्रोग्रामिंग और कोटलिन की ओर बढ़ रहे हैं। जावा एक शानदार भाषा है, लेकिन इसमें कुछ खामियां हैं जिनकी तुलना में कोटलिन और एंड्रॉइड को ऑफ़र करना पड़ता है। कोटलिन और AndroidX डेटा बाइंडिंग के उपयोग को कम से कम कर सकते हैं, अगर इसे पूरी तरह से बाहर न करें। नीचे मैं अपनी बात समझाने की कोशिश करूंगा।

Kotlin


मुझे लगता है कि कई एंड्रॉइड डेवलपर्स पहले से ही कोटलिन में बदल गए हैं, और इसलिए मेरे साथ सहमत हैं कि 2019 में कोटलिन के अलावा किसी भी भाषा में एक नया एंड्रॉइड प्रोजेक्ट लिखना समुद्र से लड़ने जैसा है। बेशक आप बहस कर सकते हैं, लेकिन फ़्लटर और डार्ट के बारे में क्या? C ++, C # और यहां तक ​​कि कॉर्डोवा के बारे में क्या? जिस पर मैं जवाब दूंगा: पसंद हमेशा आपकी होती है।

480 ई.पू. फ़ारसी राजा ज़ेरेक्स ने अपने सैनिकों को एक तूफान के दौरान अपनी सेना के हिस्से को नष्ट करने की सजा के रूप में समुद्र को पार करने का आदेश दिया, और पांच शताब्दियों बाद, रोमन सम्राट कैलीगुला ने पोसिडॉन पर युद्ध की घोषणा की। स्वाद का मामला। 10 में से 9 के लिए, कोटलिन अच्छा है, लेकिन 10 के लिए यह खराब हो सकता है। यह सब आप पर, आपकी इच्छाओं और आकांक्षाओं पर निर्भर करता है।

कोटलिन मेरी पसंद है। भाषा सरल और सुंदर है। इस पर लिखना आसान और सुखद है, और सबसे महत्वपूर्ण बात यह है कि बहुत अधिक लिखने की आवश्यकता नहीं है: डेटा क्लास, ऑब्जेक्ट, वैकल्पिक सेटर और गेट्टर, सरल लैम्ब्डा एक्सप्रेशन और एक्सटेंशन फ़ंक्शन। यह सिर्फ एक छोटा सा हिस्सा है जो इस भाषा को पेश करना है। यदि आपने अभी तक कोटलिन में स्विच नहीं किया है - जाने के लिए स्वतंत्र महसूस करें! अभ्यास के साथ अनुभाग में, मैं भाषा के कुछ फायदों का प्रदर्शन करूंगा (यह एक विज्ञापन की पेशकश नहीं है)।

मॉडल-व्यू-ViewModel


MVVM वर्तमान में Google से अनुशंसित एप्लिकेशन आर्किटेक्चर है। विकास के दौरान, हम इस विशेष पैटर्न का पालन करेंगे, हालांकि, हम इसे पूरी तरह से नहीं देखेंगे, क्योंकि MVVM डेटा बाइंडिंग का उपयोग करने की सलाह देता है, लेकिन हम इसे मना कर देते हैं।

एमवीवीएम के पेशेवरों

  • व्यापार तर्क और यूआई का अंतर। एमवीवीएम के सही कार्यान्वयन में, एंड्रॉइड या जेटपैक पैकेज से लाइवडाटा ऑब्जेक्ट्स को छोड़कर, व्यूमॉडल में एक भी आयात एंड्रॉइड नहीं होना चाहिए। समुचित उपयोग स्वचालित रूप से टुकड़ों और गतिविधियों के अंदर सभी यूआई काम छोड़ देता है। क्या यह महान नहीं है?
  • एनकैप्सुलेशन का स्तर पंप किया जाता है। एक टीम के रूप में काम करना आसान होगा: अब आप सभी एक स्क्रीन पर एक साथ काम कर सकते हैं और एक-दूसरे के साथ हस्तक्षेप नहीं कर सकते। जबकि एक डेवलपर स्क्रीन के साथ काम करता है, दूसरा एक ViewModel का निर्माण कर सकता है, और तीसरा रिपॉजिटरी में प्रश्न लिख सकता है।
  • MVVM पर इकाई परीक्षण लिखने का सकारात्मक प्रभाव पड़ता है। यह आइटम पिछले एक से चलता है। यदि सभी वर्गों और विधियों को UI के साथ काम करने से समझाया जाता है, तो उन्हें आसानी से परखा जा सकता है।
  • स्क्रीन रोटेशन के साथ एक प्राकृतिक समाधान। कोई फर्क नहीं पड़ता कि यह कितना अजीब लग सकता है, लेकिन यह सुविधा स्वचालित रूप से अधिग्रहित है, एमवीवीएम के संक्रमण के साथ (क्योंकि डेटा व्यूमॉडल में संग्रहीत है)। यदि आप काफी लोकप्रिय एप्लिकेशन (VK, Telegram, Sberbank-Online और Aviasales) की जांच करते हैं, तो यह पता चलता है कि उनमें से आधे स्क्रीन को घुमाने में सक्षम नहीं हैं। जो मुझे इन अनुप्रयोगों के उपयोगकर्ता के रूप में कुछ आश्चर्य और गलतफहमी का कारण बनता है।

MVVM क्यों है खतरनाक?

  • स्मृति रिसाव। यदि आप LiveData और पर्यवेक्षक का उपयोग करने के नियमों को तोड़ते हैं तो यह खतरनाक त्रुटि होती है। हम अभ्यास अनुभाग में इस त्रुटि की विस्तार से जांच करेंगे।
  • विशाल दृश्यमॉडल। यदि आप ViewModel में सभी व्यावसायिक तर्क फिट करने का प्रयास करते हैं, तो आपको एक अपठनीय कोड मिलेगा। इस स्थिति से बाहर निकलने का तरीका व्यूमॉडल को एक पदानुक्रम में विभाजित करना, या प्रस्तुतकर्ताओं का उपयोग करना हो सकता है। ठीक यही मैंने किया।

MVVM के साथ काम करने के नियम

आइए सबसे ब्लंडर्स से शुरू करें और कम ब्लंडर्स पर जाएं:

  • अनुरोध निकाय ViewModel (केवल रिपॉजिटरी में) में नहीं होना चाहिए;
  • LiveData ऑब्जेक्ट ViewModel में परिभाषित किए गए हैं, वे खुद को रिपॉजिटरी के अंदर नहीं फेंकते हैं, क्योंकि रिपॉजिटरी में अनुरोधों को आरएक्स-जावा (या कोरआउट) का उपयोग करके संसाधित किया जाता है;
  • सभी प्रोसेसिंग फ़ंक्शंस को थर्ड-पार्टी क्लासेस और फ़ाइलों ("प्रेज़ेटर्स") में ले जाना चाहिए, ताकि व्यूमॉडल को अव्यवस्थित न करें और सार से विचलित न हों।

LiveData


LiveData एक अवलोकन योग्य डेटा धारक वर्ग है। एक नियमित अवलोकन के विपरीत, LiveData जीवनचक्र के प्रति जागरूक है, जिसका अर्थ है कि यह अन्य ऐप घटकों, जैसे गतिविधियों, अंशों या सेवाओं के जीवनचक्र का सम्मान करता है। यह जागरूकता LiveData को केवल ऐप घटक पर्यवेक्षकों को अपडेट करती है जो एक सक्रिय जीवन चक्र की स्थिति में हैं।
स्रोत: developer.android.com/topic/lbooks/altecture/livedata

एक सरल निष्कर्ष परिभाषा से लिया जा सकता है: LiveData एक विश्वसनीय प्रतिक्रियाशील प्रोग्रामिंग टूल है। हम इसका यूज बिना डेटा बाइंडिंग के यूआई पार्ट को अपडेट करने के लिए करेंगे। ऐसा क्यों?

एक्सएमएल फाइलों की संरचना <डेटा> ... </ डेटा> से प्राप्त डेटा के एक संक्षिप्त वितरण की अनुमति नहीं देती है। यदि छोटी फ़ाइलों के साथ सब कुछ स्पष्ट है, तो बड़ी फ़ाइलों के बारे में क्या? जटिल स्क्रीन के साथ क्या करना है, कई में कई फ़ील्ड शामिल हैं और गुजर रहे हैं? हर जगह मॉडल का उपयोग करें? कठोर क्षेत्र बाइंडिंग प्राप्त करें? और यदि क्षेत्र को स्वरूपित किया जाना चाहिए, तो जावा पैकेज से कॉल विधि? यह कोड को निराशाजनक और पूरी तरह से स्पेगेटी बनाता है। एमवीवीएम ने जो वादा किया था, वह बिल्कुल नहीं।

डेटा बाइंडिंग को अस्वीकार करने से UI भाग में परिवर्तन पारदर्शी हो जाएगा। सभी अपडेट सीधे पर्यवेक्षक के अंदर होंगे। क्योंकि चूंकि कोलिन कोड संक्षिप्त और स्पष्ट है, इसलिए हमें फूला हुआ पर्यवेक्षक के साथ समस्याएं नहीं आती हैं। कोड लिखना और रखरखाव आसान हो जाएगा। XML फ़ाइलों का उपयोग केवल डिज़ाइन के लिए किया जाएगा - अंदर कोई संपत्ति नहीं।

डेटा बाइंडिंग एक शक्तिशाली उपकरण है। यह कुछ समस्याओं को हल करने के लिए बहुत अच्छा है, और यह जावा के साथ अच्छी तरह से सामंजस्य स्थापित करता है, लेकिन कोटलिन के साथ ... कोटलिन के साथ, ज्यादातर मामलों में, डेटा बाइंडिंग केवल अल्पविकसित है। डेटा बाइंडिंग केवल कोड को जटिल करता है और कोई प्रतिस्पर्धात्मक लाभ नहीं देता है।

जावा में, आपके पास एक विकल्प था: या तो डेटा बाइंडिंग का उपयोग करें, या बहुत सारे बदसूरत कोड लिखें। कोटलिन में, आप viewViewById (), और साथ ही इसकी संपत्ति को दरकिनार करते हुए सीधे दृश्य तत्वों तक पहुंच सकते हैं। उदाहरण के लिए:

// Instead of TextView textView = findViewById<TextView>(R.id.textView) textView.text = "Hello, world!" textView.visibility = View.VISIBLE 

एक तार्किक सवाल उठता है: एक्सएमएल फाइलों के अंदर बागवानी मॉडल से परेशान क्यों, एक्सएमएल फाइलों में जावा विधियों को लागू करना, एक्सएमएल भाग के तर्क को ओवरलोड करना अगर यह सब टाला जा सकता है?

थ्रेड () और आरएक्स-जावा के बजाय कोरआउट


Coroutines अविश्वसनीय रूप से हल्के और उपयोग में आसान हैं। वे सबसे सरल अतुल्यकालिक कार्यों के लिए आदर्श हैं: क्वेरी परिणाम संसाधित करना, UI अपडेट करना, आदि।

कोराउटीन उन मामलों में प्रभावी ढंग से थ्रेड () और आरएक्स-जावा को बदल सकता है जहां उच्च प्रदर्शन की आवश्यकता नहीं है, क्योंकि वे गति के साथ हल्कापन के लिए भुगतान करते हैं। Rx-Java, निस्संदेह, अधिक कार्यात्मक है, हालांकि सरल कार्यों के लिए इसकी सभी परिसंपत्तियों की आवश्यकता नहीं है।

माइक्रोसॉफ्ट और बाकी


आउटलुक सेवाओं के साथ काम करने के लिए, Microsoft ग्राफ़ एपीआई का उपयोग किया जाएगा। उपयुक्त अनुमतियों के साथ, इसके माध्यम से आप कर्मचारियों, कमरों और घटना-आह (बैठकों) के बारे में सभी आवश्यक जानकारी प्राप्त कर सकते हैं। चेहरे की पहचान के लिए, Microsoft फेस एपीआई क्लाउड सेवा का उपयोग किया जाएगा।

थोड़ा आगे देखते हुए, मैं कहूंगा कि स्केलेबिलिटी की समस्या को हल करने के लिए, फायरबेस क्लाउड स्टोरेज का उपयोग किया गया था। इस पर नीचे चर्चा की जाएगी।

आर्किटेक्चर


स्केलेबिलिटी के मुद्दे


सिस्टम को पूरी तरह से या आंशिक रूप से स्केलेबल बनाना काफी मुश्किल है। यह करना विशेष रूप से मुश्किल है अगर आवेदन का पहला संस्करण स्केलेबल नहीं था, और दूसरा बनना चाहिए। अनुप्रयोग v1 ने सभी कमरों में एक ही बार अनुरोध भेजा। प्रत्येक टेबलेट नियमित रूप से सर्वर को सभी डेटा अपडेट करने के लिए अनुरोध भेजती है। उसी समय, उपकरणों ने एक दूसरे के साथ सिंक्रनाइज़ नहीं किया, क्योंकि परियोजना का अपना सर्वर नहीं है।

बेशक, अगर हम एक ही रास्ते पर जाते हैं और प्रत्येक एन टैबलेट से एन अनुरोध भेजते हैं, तो कुछ बिंदु पर हम या तो Microsoft ग्राफ़ एपीआई को पलट देंगे या हमारे सिस्टम को फ्रीज़ कर देंगे।

क्लाइंट-सर्वर समाधान का उपयोग करना तर्कसंगत होगा जिसमें सर्वर ग्राफ़ को प्रदूषित करता है, डेटा जमा करता है और अनुरोध पर, टैबलेट को जानकारी प्रदान करता है, लेकिन यहां हम वास्तविकता से मिलते हैं। प्रोजेक्ट टीम में 2 लोग (Android डेवलपर और डिज़ाइनर) शामिल हैं। उन्हें 7 सप्ताह की समय सीमा को पूरा करने की आवश्यकता है और बैकएंड प्रदान नहीं किया गया है, क्योंकि स्केलिंग डेवलपर से एक आवश्यकता है। लेकिन इसका मतलब यह नहीं है कि विचार को छोड़ दिया जाना चाहिए?

संभवतः इस स्थिति में एकमात्र सही समाधान क्लाउड स्टोरेज का उपयोग होगा। फायरबेस सर्वर की जगह लेगा और बफर की तरह काम करेगा। फिर यह निम्नलिखित को पूरा करता है: प्रत्येक टैबलेट Microsoft ग्राफ़ एपीआई से केवल अपने पते का सर्वेक्षण करता है, और यदि आवश्यक हो, तो क्लाउड स्टोरेज में डेटा को सिंक्रनाइज़ करता है, जहां से इसे अन्य उपकरणों द्वारा पढ़ा जा सकता है।

इस कार्यान्वयन का लाभ एक त्वरित प्रतिक्रिया होगी, क्योंकि Firebase रियल-टाइम मोड में काम करता है। हम सर्वर एन को भेजे गए अनुरोधों की संख्या को कम कर देंगे, जिसका अर्थ है कि डिवाइस बैटरी पर थोड़ी देर काम करेगा। वित्तीय दृष्टिकोण से, परियोजना की कीमत में वृद्धि नहीं हुई, क्योंकि इस परियोजना के लिए, फायरबेस का मुफ्त संस्करण कई भंडार के साथ पर्याप्त है: 1 जीबी स्टोरेज, प्रति माह 10 हजार प्राधिकरण और एक बार में 100 कनेक्शन। नुकसान में थर्ड-पार्टी फ्रेमवर्क पर निर्भरता शामिल हो सकती है, लेकिन फायरबेस हमें विश्वास दिलाता है, क्योंकि यह Google द्वारा बनाए और विकसित एक स्थिर उत्पाद है।

नई प्रणाली का सामान्य विचार इस प्रकार था: एन टैबलेट और वास्तविक समय के डेटा सिंक्रनाइज़ेशन के लिए क्लाउड प्लेटफ़ॉर्म। आइए आवेदन को स्वयं डिजाइन करना शुरू करें।

लाइवडॉट इन रिपोजिटरी


ऐसा लगता है कि मैंने हाल ही में अच्छे फॉर्म के नियम स्थापित किए हैं और उनमें से एक का तुरंत उल्लंघन किया है। ViewModel के अंदर LiveData के अनुशंसित उपयोग के विपरीत, इस परियोजना में LiveData ऑब्जेक्ट्स को रिपॉजिटरी में आरंभीकृत किया जाता है, और सभी रिपॉजिटरी को सिंगलटन के रूप में घोषित किया जाता है। ऐसा क्यों?

एक समान समाधान एप्लिकेशन मोड के साथ जुड़ा हुआ है। गोलियाँ सुबह 8 से शाम 8 बजे तक खुली रहती हैं। इस समय, उन पर केवल मीटिंग रूम हेल्पर लॉन्च किया गया है। नतीजतन, कई ऑब्जेक्ट लंबे समय तक जीवित रह सकते हैं (यही कारण है कि सभी रिपॉजिटरी को सिंगलटन के रूप में डिज़ाइन किया गया है)।

काम के दौरान, यूआई सामग्री को नियमित रूप से स्विच किया जाता है, जो बदले में ViewModel ऑब्जेक्ट्स के निर्माण और मनोरंजन को मजबूर करता है। यह पता चला है कि यदि आप ViewModel के अंदर LiveData का उपयोग करते हैं, तो प्रत्येक बनाए गए टुकड़े के लिए अपने ViewModel को निर्दिष्ट LiveData ऑब्जेक्ट के एक सेट के साथ बनाया जाएगा। यदि स्क्रीन पर एक जैसे अलग ViewModel और एक सामान्य Base-ViewModel के साथ 2 समान अंश प्रदर्शित किए जाते हैं, तो प्रारंभ के दौरान Base-ViewModel से LiveData ऑब्जेक्ट का दोहराव होगा। भविष्य में, ये डुप्लिकेट मेमोरी स्पेस तब तक लेंगे जब तक वे "कचरा संग्रहकर्ता" द्वारा नष्ट नहीं हो जाते। क्योंकि यदि हमारे पास पहले से ही एक सिंगलटन के रूप में एक भंडार है और हम स्क्रीन को फिर से बनाने की लागत को कम करना चाहते हैं, तो LiveData वस्तुओं को एक सिंगलटन-रिपॉजिटरी में स्थानांतरित करना बुद्धिमान होगा, जिससे ViewModel ऑब्जेक्ट्स की सुविधा होगी और एप्लिकेशन को गति मिलेगी।

बेशक, इसका मतलब यह नहीं है कि आपको ViewModel से रिपॉजिटरी में सभी LiveData को स्थानांतरित करने की आवश्यकता है, लेकिन आपको इस मुद्दे पर अधिक सावधानी से संपर्क करना चाहिए और अपनी पसंद को सचेत रूप से करना चाहिए। इस दृष्टिकोण का नुकसान लंबे समय से जीवित वस्तुओं की संख्या में वृद्धि है, क्योंकि सभी रिपॉजिटरी को सिंगलटन के रूप में परिभाषित किया गया है और उनमें से प्रत्येक LiveData ऑब्जेक्ट को संग्रहीत करता है। लेकिन एक विशेष मामले में, मीटिंग रूम हेल्पर माइनस नहीं है, क्योंकि अनुप्रयोग अन्य अनुप्रयोगों के संदर्भ में स्विच किए बिना, पूरे दिन नॉन-स्टॉप चलता है।

परिणाम वास्तुकला




  • सभी अनुरोधों को रिपॉजिटरी में निष्पादित किया जाता है। सभी रिपॉजिटरी (मीटिंग रूम हेल्पर में उनमें से 11 हैं) को सिंगलटन के रूप में डिज़ाइन किया गया है। वे लौटे ऑब्जेक्ट्स के प्रकार से विभाजित होते हैं और facades के पीछे छिपे होते हैं।
  • व्यावसायिक तर्क ViewModel में रहता है। "प्रेजेंटर्स" के उपयोग के लिए धन्यवाद, सभी ViewModel (परियोजना में 6 हैं) का कुल आकार 120 रेखाओं से कम निकला।
  • गतिविधि और टुकड़े केवल UI भाग को बदलने में शामिल हैं, पर्यवेक्षक और LiveData का उपयोग करके ViewModel से लौटे हैं।
  • डेटा के प्रसंस्करण और निर्माण के लिए कार्य "प्रस्तुतकर्ता" में संग्रहीत किए जाते हैं। डेटा प्रोसेसिंग के लिए कोटलिन से सक्रिय रूप से अनुमति कार्यों का उपयोग किया जाता है।

पृष्ठभूमि तर्क को आशय-सेवा में स्थानांतरित कर दिया गया है:

  • घटना अद्यतन सेवा। फायरबेस और ग्राफ़ एपीआई में वर्तमान कमरे के डेटा को सिंक्रनाइज़ करने के लिए जिम्मेदार सेवा।
  • उपयोगकर्ता-पहचानो-सेवा। केवल मास्टर टैबलेट पर चलता है। सिस्टम में नए स्टाफ को जोड़ने के लिए जिम्मेदार। सक्रिय निर्देशिका की सूची के साथ पहले से ही प्रशिक्षित व्यक्तियों की एक सूची की जाँच करता है। यदि नए लोग दिखाई देते हैं, तो सेवा उन्हें फेस एपीआई में जोड़ देती है और तंत्रिका नेटवर्क को वापस कर देती है। ऑपरेशन पूरा होने पर, यह अक्षम है। यह शुरू होता है जब आवेदन शुरू होता है।
  • ऑनलाइन-अधिसूचना-सेवा अन्य टैबलेट्स को सूचित करती है कि यह टैबलेट कार्य कर रहा है, अर्थात। बाहरी बैटरी समाप्त नहीं हुई है। यह फायरबेस के माध्यम से काम करता है।

परिणाम जिम्मेदारियों के वितरण के दृष्टिकोण से एक बल्कि लचीला और सही वास्तुकला था, जो आधुनिक विकास की सभी आवश्यकताओं को पूरा करता है। यदि भविष्य में हम Microsoft ग्राफ़ एपीआई, फायरबेस या किसी अन्य मॉड्यूल को छोड़ देते हैं, तो उन्हें आसानी से नए एप्लिकेशन के साथ हस्तक्षेप किए बिना बदल दिया जा सकता है। "प्रेजेंटर्स" की एक व्यापक प्रणाली की उपस्थिति ने कोर से परे सभी डेटा प्रोसेसिंग कार्यों को लेना संभव बना दिया। नतीजतन, आर्किटेक्चर क्रिस्टल स्पष्ट हो गया है, जो एक बड़ा प्लस है। एक अतिवृष्टि ViewModel की समस्या पूरी तरह से गायब हो गई है।

नीचे मैं एक विकसित अनुप्रयोग में आमतौर पर इस्तेमाल किए जाने वाले बंडल का एक उदाहरण दूंगा।

अभ्यास। अपडेट देखें


बैठक कक्ष की स्थिति के आधार पर, डायल निम्न स्थितियों में से एक दिखाता है:




इसके अलावा, रैलियों के अस्थायी आर्क डायल की रूपरेखा के साथ स्थित हैं, और केंद्र बैठक के अंत तक या अगली रैली की शुरुआत तक नीचे गिना जाता है। यह सब हमारे द्वारा विकसित किए गए कैनवास पुस्तकालय द्वारा किया जाता है। यदि मीटिंग्स का ग्रिड बदल गया है, तो हमें लाइब्रेरी में डेटा को अपडेट करना होगा।

चूंकि लाइवडॉट की घोषणा रिपोजिटरी में की गई है, इसलिए उनके साथ शुरू करना सबसे तर्कसंगत है।

डेटा संग्रह स्थान


FirebaseRoomRepository - रूम मॉडल से संबंधित फायरबेस में अनुरोध भेजने और प्रसंस्करण के लिए जिम्मेदार एक वर्ग।

 // 1. object FirebaseRoomRepository { private val database = FirebaseFactory.database val rooms: MutableList<Room> = ArrayList() // 2. var currentRoom: MutableLiveData<Room?> = MutableLiveData() val onlineStatus: MediatorLiveData<HashMap<String, Boolean>> = MediatorLiveData() var otherRooms: MutableLiveData<List<Room>> = MutableLiveData() var ownRoom: MutableLiveData<Room> = MutableLiveData() // 3. private val roomsListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { updateRooms(dataSnapshot) } override fun onCancelled(error: DatabaseError) {} } init { // 4. database.getReference(ROOMS_CURRENT_STATES) .addValueEventListener(roomsListener) } // 5. private fun updateRooms(dataSnapshot: DataSnapshot) { rooms.updateRooms(dataSnapshot) otherRooms.updateOtherRooms(rooms) ownRoom.updateOwnRoom(rooms) currentRoom.updateCurrentRoom(rooms, ownRoom) } } 

प्रदर्शित करने के लिए, श्रोता फायरबेस आरंभीकरण कोड को थोड़ा सरल किया गया था (पुन: कनेक्ट फ़ंक्शन हटा दिया गया था)। आइए एक नज़र डालते हैं कि यहाँ क्या हो रहा है:

  1. रिपॉजिटरी को एक सिंगलटन के रूप में डिज़ाइन किया गया है (कोटलिन में, यह क्लास कीवर्ड को ऑब्जेक्ट के साथ बदलने के लिए पर्याप्त है);
  2. LiveData वस्तुओं का आरंभीकरण;
  3. ValueEventListener को फिर से बनाने के मामले में एक अनाम वर्ग फिर से बनाने से बचने के लिए एक चर के रूप में घोषित किया गया है (याद रखें, मैंने वियोग के मामले में पुन: संयोजन को हटाकर प्रारंभिक सरलीकरण किया है);
  4. ValueEventListener (यदि फायरबेस में डेटा बदलता है, तो श्रोता लाइव डेटा में ऑब्जेक्ट्स को तुरंत निष्पादित और अपडेट करेगा);
  5. LiveData वस्तुओं के लिए अद्यतन।

कार्यों को स्वयं एक अलग FirebaseRoomRepositoryPresenter फ़ाइल में ले जाया जाता है और विस्तार कार्यों के रूप में सजाया जाता है।

 fun MutableLiveData<List<Room>>.updateOtherRooms(rooms: MutableList<Room>) { this.postValue(rooms.filter { !it.isOwnRoom() }) } 

एक्सटेंशन फ़ंक्शन उदाहरण FirebaseRoomRepositoryPresenter से

इसके अलावा, चित्र की सामान्य समझ के लिए, मैं कक्ष ऑब्जेक्ट की एक सूची दूंगा।

 // 1. data class Room(var number: String = "", var nickName: String = "", var email: String? = null, var imgSmall: String? = null, var imgOffline: String? = null, var imgFree: String? = null, var imgWait: String? = null, var imgBusy: String? = null, var events: List<Event.Short> = emptyList()) // 2. 

  1. डेटा वर्ग। यह संशोधक स्वचालित रूप से उत्पन्न (और), हैशकोड (), और समान () विधियों से आगे निकल जाता है। अब उन्हें खुद को फिर से परिभाषित करने की कोई आवश्यकता नहीं है।
  2. कक्ष सूची से घटनाक्रम सूची। यह वह सूची है जो डायल लाइब्रेरी में डेटा को अपडेट करने के लिए आवश्यक है।

सभी रिपॉजिटरी कक्षाएं मुखौटा वर्ग के पीछे छिपी हुई हैं।

 object Repository { // 1. private val firebaseRoomRepository = FirebaseRoomRepository // ......... /** * Rooms queries */ fun getOtherRooms() = firebaseRoomRepository.otherRooms fun getOwnRoom() = firebaseRoomRepository.ownRoom fun getAllRooms() = firebaseRoomRepository.rooms // 2. fun getCurrentRoom() = firebaseRoomRepository.currentRoom //   // ....... } 

  1. ऊपर आप सभी उपयोग किए गए रिपॉजिटरी वर्गों और दूसरे स्तर के facades की एक सूची देख सकते हैं। यह कोड की सामान्य समझ को सरल करता है और सभी जुड़े हुए रिपॉजिटरी वर्गों की सूची प्रदर्शित करता है।
  2. उन विधियों की एक सूची जो FirebaseRoomRepository से LiveData ऑब्जेक्ट्स का संदर्भ देती है। कोटलिन के निवासी और गेटर्स वैकल्पिक हैं, इसलिए आपको उन्हें अनावश्यक रूप से लिखने की आवश्यकता नहीं है।

ऐसा संगठन आपको एक रूट रिपॉजिटरी में 20 से 30 अनुरोधों से आराम से फिट होने की अनुमति देता है। यदि आपके आवेदन में अधिक अनुरोध हैं, तो आपको रूट मुखौटा को 2 या अधिक में विभाजित करना होगा।

ViewModel


BaseViewModel आधार ViewModel है जहाँ से सभी ViewModel को विरासत में मिला है। इसमें सार्वभौमिक रूप से उपयोग की जाने वाली एक सिंगल करंट ऑब्जेक्ट शामिल है।

 // 1. open class BaseViewModel : ViewModel() { // 2. fun getCurrentRoom() = Repository.getCurrentRoom() } 

  1. ओपन मार्कर का मतलब है कि आप क्लास से वारिस हो सकते हैं। कोटलिन में डिफ़ॉल्ट रूप से, सभी कक्षाएं और विधियां अंतिम हैं, अर्थात। कक्षाएं विरासत में नहीं ली जा सकती हैं, और तरीकों को फिर से परिभाषित नहीं किया जा सकता है। यह आकस्मिक असंगत संस्करण परिवर्तनों से बचाने के लिए है। मैं एक उदाहरण दूंगा।

    आप लाइब्रेरी का एक नया संस्करण विकसित कर रहे हैं। कुछ बिंदु पर, एक कारण या किसी अन्य के लिए, आप वर्ग का नाम बदलने या किसी विधि के हस्ताक्षर को बदलने का निर्णय लेते हैं। इसे बदलकर, आपने गलती से संस्करण असंगति बना दिया। उफ़ ... यदि आप शायद जानते थे कि विधि किसी के द्वारा ओवरराइड की जा सकती है और वर्ग को विरासत में मिली है, तो आप शायद अधिक सटीक होते और शायद ही पैर में गोली मारते। ऐसा करने के लिए, कोटलिन में, डिफ़ॉल्ट रूप से, सब कुछ अंतिम रूप में घोषित किया गया है, और रद्द करने के लिए एक "खुला" संशोधक है।
  2. GetCurrentRoom () विधि रिपॉजिटरी से करंट रूम के लाइवडेटा ऑब्जेक्ट के लिए एक लिंक देता है, जो बदले में, FirebaseRoomRepository से लिया जाता है। जब इस पद्धति को कॉल किया जाता है, तो कक्ष ऑब्जेक्ट घटनाओं की सूची सहित, कमरे के बारे में सभी जानकारी युक्त वापस आ जाएगा।

डेटा को एक प्रारूप से दूसरे में बदलने के लिए, हम परिवर्तन का उपयोग करेंगे। ऐसा करने के लिए, एक MainFragmentViewModel बनाएँ और इसे BaseViewModel से विरासत में मिला

MainFragmentViewModel BaseViewModel का एक व्युत्पन्न वर्ग है। इस ViewModel का उपयोग केवल MainFragment में किया जाता है।

 // 1. class MainFragmentViewModel: BaseViewModel () { // 2. var currentRoomEvents = Transformations.switchMap(getCurrentRoom()) { val events: MutableLiveData<List<Event.Short>> = MutableLiveData() // some business logic events.postValue(it?.eventsList) events } // 3. val currentRoomEvents2 = MediatorLiveData<List<Event.Short>>().apply { addSource(getCurrentRoom()) { room -> // some business logic postValue(room?.eventsList) } } } 

  1. खुले संशोधक की कमी पर ध्यान दें। इसका मतलब है कि कोई भी वर्ग से विरासत में नहीं मिला है।
  2. currentRoomEvents - परिवर्तन का उपयोग करके प्राप्त एक वस्तु। जैसे ही करंट रूम का ऑब्जेक्ट बदलता है, ट्रांसफॉर्मेशन हो जाता है और करंट रुम ऑब्जेक्ट अपडेट हो जाता है।
  3. MediatorLiveData। परिणाम परिवर्तन के समान है (संदर्भ के लिए दिखाया गया है)।

पहले विकल्प का उपयोग डेटा को एक प्रकार से दूसरे में बदलने के लिए किया जाता है, जो कि हमें चाहिए, और दूसरा विकल्प कुछ व्यावसायिक तर्क को निष्पादित करने के लिए आवश्यक है। हालाँकि, डेटा रूपांतरण नहीं होता है। याद रखें कि ViewModel में Android आयात मान्य नहीं है। इसलिए, मैं यहां से अतिरिक्त अनुरोध शुरू करता हूं या आवश्यक के रूप में सेवाओं को पुनरारंभ करता हूं।

महत्वपूर्ण सूचना! कार्य करने के लिए परिवर्तन या मध्यस्थ के लिए, किसी को खंड या गतिविधि से उनकी सदस्यता लेनी चाहिए। अन्यथा, कोड निष्पादित नहीं किया जाएगा, क्योंकि कोई भी परिणाम की उम्मीद नहीं करेगा (ये ऑब्जर्वर ऑब्जेक्ट हैं)।

MainFragment


परिणाम में डेटा परिवर्तित करने में अंतिम चरण। MainFragment में स्क्रीन के नीचे एक डायल लाइब्रेरी और एक व्यू-पेजर शामिल है।

 class MainFragment : BaseFragment() { // 1. private lateinit var viewModel: MainFragmentViewModel // 2. private val currentRoomObserver = Observer<List<Event.Short>> { clockView.updateArcs(it) } override fun onAttach(context: Context?) { super.onAttach(context) // 3. viewModel = ViewModelProviders.of(this).get(MainFragmentViewModel::class.java) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_main, container, false) } override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) // 4. viewModel.currentRoomEvents.observe(viewLifecycleOwner, currentRoomObserver) } } 

  1. MainFragmentViewModel का प्रारंभ। लेटइनिट संशोधक इंगित करता है कि हम इस वस्तु को बाद में उपयोग करने से पहले वादा करते हैं। कोटलिन प्रोग्रामर को गलत कोड लेखन से बचाने की कोशिश करता है, इसलिए हमें या तो तुरंत यह कहना होगा कि ऑब्जेक्ट अशक्त हो सकता है, या लेटेरिट डाल सकता है। इस स्थिति में, ViewModel को ऑब्जेक्ट द्वारा प्रारंभ किया जाना चाहिए।
  2. डायल को अपडेट करने के लिए ऑब्जर्वर-श्रोता।
  3. ViewModel की शुरुआत। कृपया ध्यान दें कि यह तब होता है जब टुकड़ा गतिविधि से जुड़ा होता है।
  4. गतिविधि बनने के बाद, हम currentRoomEvents ऑब्जेक्ट में परिवर्तन की सदस्यता लेते हैं। कृपया ध्यान दें कि मैं खंड जीवन चक्र (यह) की सदस्यता नहीं लेता, लेकिन viewLifecycleOwner ऑब्जेक्ट के लिए। तथ्य यह है कि समर्थन पुस्तकालय में 28.0.0 और AndroidX 1.0.0 एक बग का पता चला था जब पर्यवेक्षक "बिना सदस्यता के" था। इस समस्या को हल करने के लिए, viewLifecycleOwner के रूप में एक पैच जारी किया गया था, और Google इसकी सदस्यता लेने की सिफारिश करता है। यह ज़ोंबी पर्यवेक्षक की समस्या को ठीक करता है जब टुकड़ा मर गया और पर्यवेक्षक काम करना जारी रखता है। यदि आप अभी भी इसका उपयोग कर रहे हैं, तो इसे viewLifecycleOwner के साथ बदलना सुनिश्चित करें।

इस प्रकार, मैं डेटा बाध्यकारी का उपयोग किए बिना MVVM और LiveData की सादगी और सुंदरता का प्रदर्शन करना चाहता हूं। कृपया ध्यान दें कि इस परियोजना में मैं परियोजना की बारीकियों के कारण लाइवडेटा को रिपोजिटरी में रखकर आम तौर पर स्वीकृत नियम का उल्लंघन करता हूं। हालाँकि, अगर हम उन्हें ViewModel में ले गए, तो समग्र चित्र अपरिवर्तित रहेगा।

एक केक पर चेरी के रूप में, मैंने आपके लिए एक प्रदर्शन के साथ एक छोटा वीडियो तैयार किया है (सुरक्षा आवश्यकताओं के अनुसार नाम स्मियर किए जाते हैं, मैं माफी माँगता हूँ):




परिणाम


पहले महीने में आवेदन के परिणामस्वरूप, क्रॉस रैलियों के प्रदर्शन में कुछ बग सामने आए (आउटलुक आपको एक ही समय में कई ईवेंट बनाने की अनुमति देता है, जबकि हमारा सिस्टम नहीं करता है)। अब यह प्रणाली 3 महीने से काम कर रही है। त्रुटियां या विफलताएं नहीं देखी जाती हैं।

टिप्पणी के लिए PS धन्यवाद jericho_code कोटलिन में, आप खाली सूची () का उपयोग करके मॉडल में सूची <> को प्रारंभ कर सकते हैं, फिर एक अतिरिक्त ऑब्जेक्ट नहीं बनाया जाता है।
 var events: List<Event.Short> = emptyList() //      EmptyList var events: List<Event.Short> = ArrayList() //    

Source: https://habr.com/ru/post/hi457960/


All Articles