توضح هذه المقالة بالتفصيل مراحل تطوير تطبيق المحمول Meeting Room Helper: من بداية الفكرة إلى الإصدار. تتم كتابة التطبيق في Kotlin وبنيت على بنية MVVM مبسطة ، دون استخدام ربط البيانات. يتم تحديث جزء واجهة المستخدم باستخدام كائنات LiveData. أسباب رفض ربط البيانات مفصّلة وموضحة. تستخدم البنية عددًا من الحلول المثيرة للاهتمام التي تجعل من الممكن تقسيم البرنامج بشكل منطقي إلى ملفات صغيرة ، مما يبسط في النهاية دعم الكود.


وصف المشروع
قبل 3 سنوات ، توصلت شركتنا إلى فكرة تطوير مشروع صغير للحجز الفوري لقاعات الاجتماعات. يفضل معظم مديري الموارد البشرية و Arcadia استخدام تقويم Outlook لمثل هذه الأغراض ، ولكن ماذا عن البقية؟
سأقدم مثالين من حياة المطور
- أي فريق لديه رغبة عفوية في عقد تجمع سريع لمدة 5-10 دقائق. يمكن لهذه الرغبة أن تتفوق على المطورين في أي ركن من أركان المكتب ، وحتى لا يشتت انتباه الزملاء من حولهم ، فإنهم (المطورون وليس فقط) يبدؤون في البحث عن محادثة مجانية. يهاجرون من غرفة إلى أخرى (في مكتبنا يتم ترتيب قاعات الاجتماع على التوالي) ، "يقوم الزملاء بالتحقق بعناية" من أي غرفة متاحة حاليًا. نتيجة لذلك ، يصرف الزملاء من الداخل. كان هؤلاء الأشخاص دائمًا وسيظلون كذلك ، حتى لو تم تنفيذ عملية الإعدام في ميثاق الشركة الخاص بوقف التجمع. الذي فهم ، وقال انه سوف يفهم.
- وهنا حالة أخرى. لقد تركت غرفة الطعام لتتوجه إلى نفسك ، ولكن هنا يعترض عليك زميلك (أو مديرك) من قسم آخر. إنه يريد أن يخبرك بشيء عاجل ، ولهذه الأغراض تحتاج إلى غرفة اجتماعات. وفقًا للوائح ، يجب أولاً حجز غرفة (من هاتفك أو جهاز الكمبيوتر) ثم شغلها بعد ذلك. من الجيد أن يكون لديك هاتف جوال به Outlook Mobile. وإذا لم يكن كذلك؟ العودة إلى الكمبيوتر ، ثم مرة أخرى للعودة إلى غرفة الاجتماعات؟ لإجبار كل موظف على وضع Outlook Express على الهاتف والتأكد من أن الجميع يحمل الهواتف معهم؟ هذه ليست طرقنا.
هذا هو السبب في أنه قبل 2.5 عام تم تجهيز كل غرفة من غرف الاجتماعات بأجهزة الكمبيوتر اللوحي الخاصة بها:

بالنسبة لهذا المشروع ، قام زميلي بتطوير الإصدار الأول من التطبيق: Meeting Room Little Helper (
هنا يمكنك أن تقرأ عنه ). يسمح MRLH لحجز الحجز ، وإلغاء وتجديد الحجز ، وأظهر حالة المحادثات المتبقية. أصبح التعرف على هوية الموظف (باستخدام خدمة Microsoft Face API السحابية ومحللاتنا الداخلية) بمثابة "خدعة" مبتكرة. تبين أن التطبيق قوي وخدم الشركة بأمانة لمدة 2.5 عام.
ولكن مرور الوقت ... ظهرت أفكار جديدة. أردت شيئًا جديدًا ، ولذا قررنا إعادة كتابة التطبيق.
الاختصاصات
كما يحدث في كثير من الأحيان - ولكن لسوء الحظ ، ليس دائمًا - بدأ التطوير بإعداد المواصفات الفنية. بادئ ذي بدء ، اتصلنا اللاعبين الذين يستخدمون في معظم الأحيان أقراص للحجز. لقد حدث أن معظمهم كانوا مدمنين على الموارد البشرية والمديرين الذين سبق لهم استخدام Outlook حصريًا. تلقينا منهم التعليقات التالية (من المتطلبات ، من الواضح على الفور ما طلبته الموارد البشرية وما طلبه المديرون):
- يجب عليك إضافة القدرة على حجز أي غرفة اجتماعات من أي جهاز لوحي (سابقًا ، سمح لك كل قرص بحجز غرفتك فقط) ؛
- سيكون من الرائع أن ننظر إلى جدول المسيرات لعقد اجتماع طوال اليوم (من الناحية المثالية ، لأي يوم) ؛
- يجب تنفيذ دورة التطوير بالكامل في وقت قصير (لمدة 6-7 أسابيع).
كل شيء واضح مع رغبات العميل ، ولكن ماذا عن المتطلبات الفنية والمستقبل؟ أضف بعض المتطلبات للمشروع من نقابة المطورين:
- يجب أن يعمل النظام مع كل من الأجهزة اللوحية الحالية وأخرى جديدة ؛
- قابلية التوسع للنظام - من 50 محادثة وما فوق (يجب أن يكون هذا كافيًا بهامش لمعظم العملاء إذا بدأ النظام في النسخ المتماثل) ؛
- الحفاظ على الوظيفة السابقة (استخدم الإصدار الأول من التطبيق واجهة برمجة تطبيقات Java للتواصل مع خدمات Outlook ، وخططنا لاستبدالها بواجهة برمجة تطبيقات Microsoft Graph ، لذلك كان من المهم عدم فقدان الوظيفة) ؛
- تقليل استهلاك الطاقة (يتم تشغيل الأجهزة اللوحية ببطارية خارجية ، لأن مركز الأعمال لم يسمح بحفر جدرانه لوضع الأسلاك لدينا) ؛
- تصميم UX / UI جديد ، يعكس هندسيًا جميع الابتكارات.
المجموع 8 نقاط. متطلبات عادلة إلى حد ما. بالإضافة إلى ذلك ، نضع قواعد التطوير العامة:
- استخدام التقنيات المتقدمة فقط (سيسمح ذلك للفريق بالتطور كمتخصصين وليس الركود في مكان واحد ، مع تبسيط دعم المشروع في المستقبل المنظور) ؛
- اتباع أفضل الممارسات ، ولكن لا تأخذها بشكل عمياء كأمر مسلم به ، كما القاعدة الرئيسية لأي محترف (ومطور يسعى لذلك) هو تقييم كل شيء بشكل نقدي ؛
- كتابة رمز نظيف ومرتب (ربما يكون هذا هو الأكثر صعوبة عندما تحاول الجمع بين الابتكار وضيق وقت التطوير).
لقد تم البدء. كما هو الحال دائما ، هو متحمس! دعونا نرى ما سيحدث بعد ذلك.
تصميم
تصميم التطبيقات التي طورها مصمم UX:


هذه هي الشاشة الرئيسية. سيتم عرضه معظم الوقت. جميع المعلومات الضرورية موجودة بشكل مريح هنا:
- اسم الغرفة ورقمها ؛
- الوضع الحالي
- الوقت حتى الاجتماع التالي (أو حتى نهايته) ؛
- أوضاع الغرف المتبقية في أسفل الشاشة.
يرجى ملاحظة: الاتصال الهاتفي يعرض فقط 12 ساعة ، كما تم تكوين النظام وفقًا لاحتياجات الشركة (تعمل أقراص Arcadia من الساعة 8 صباحًا إلى 8 مساءً ، وتشغيلها وإيقافها تلقائيًا)


لحجز غرفة ، ما عليك سوى الاتصال بنافذة الحجز والإشارة إلى مدة التجمع. خطوات حجز الغرف المتبقية متشابهة ، فهي تبدأ فقط بالنقر على أيقونة الغرفة.


إذا كنت ترغب في جدولة اجتماع لفترة محددة ، فانتقل إلى علامة التبويب التالية ، في قائمة الاجتماعات التي ستعقد اليوم في غرفة الاجتماعات ، وانقر على وقت الفراغ. علاوة على ذلك ، كل شيء كما في الحالة الأولى.
يجب أن تبدو شجرة الانتقال الكاملة بالشكل التالي:


دعونا نحاول تنفيذه بكفاءة.
كومة التكنولوجيا
تطوير تقنيات تتطور بسرعة إلى حد ما وتتغير. لمدة عامين آخرين ، كانت Java هي لغة تطوير Android الرسمية. كتب الجميع في جافا واستخدام البيانات ملزمة. الآن ، يبدو لي ، أننا نتحرك نحو البرمجة التفاعلية وكوتلين. تعد Java لغة رائعة ، لكنها تحتوي على بعض العيوب مقارنة بما تقدمه Kotlin و AndroidX. يمكن أن يقلل Kotlin و AndroidX من استخدام ربط البيانات إلى الحد الأدنى ، إن لم يكن يستبعده تمامًا. أدناه سأحاول شرح وجهة نظري.
Kotlin
أعتقد أن العديد من مطوري Android قد تحولوا بالفعل إلى Kotlin ، وبالتالي أتفق معي في أن كتابة مشروع Android جديد في عام 2019 بأي لغة أخرى غير Kotlin يشبه القتال في البحر. بالطبع يمكنك القول ، ولكن ماذا عن رفرفة ودارت؟ ماذا عن C ++ ، C # ، وحتى Cordova؟ الذي سأجيب عليه: الخيار لك دائمًا.
في 480 قبل الميلاد أمر الملك الفارسي زيركسيس جنوده بعبور البحر كعقاب لتدمير جزء من جيشه خلال العاصفة ، وبعد خمسة قرون ، أعلن الإمبراطور الروماني كاليجولا الحرب على بوسيدون. مسألة ذوق. بالنسبة لـ 9 من أصل 10 ، تعتبر Kotlin جيدة ، لكن بالنسبة إلى 10 قد تكون سيئة. كل هذا يتوقف عليك ، على رغباتك وتطلعاتك.
Kotlin هو خياري. اللغة بسيطة وجميلة. الكتابة عليه سهلة وممتعة ، والأهم من ذلك ، ليست هناك حاجة للكتابة أكثر من اللازم: فئة البيانات ، الكائن ، أداة الضبط والجمع الاختيارية ، تعبيرات lambda البسيطة ووظائف الامتداد. هذا مجرد جزء صغير مما تقدمه هذه اللغة. إذا لم تكن قد تحولت إلى Kotlin بعد - فلا تتردد في الذهاب! في القسم مع الممارسة ، سأوضح بعض مزايا اللغة (ليس عرضًا إعلانيًا).
نموذج للرؤية وViewModel
MVVM هي حاليًا بنية التطبيق الموصى بها من Google. أثناء التطوير ، سنلتزم بهذا النمط المحدد ، ومع ذلك ، لن نلتزم به تمامًا ، حيث توصي MVVM باستخدام ربط البيانات ، لكننا نرفضه.
إيجابيات MVVM- التفريق بين منطق الأعمال و UI. في التطبيق الصحيح لـ MVVM ، لا ينبغي أن يكون هناك أندرويد استيراد واحد في ViewModel ، باستثناء كائنات LiveData من حزم AndroidX أو Jetpack. الاستخدام السليم يترك تلقائيا كل عمل واجهة المستخدم داخل شظايا والأنشطة. أليس هذا رائعا؟
- يتم ضخ مستوى التغليف. سيكون من الأسهل العمل كفريق واحد: يمكنك الآن العمل معًا على شاشة واحدة وعدم التدخل مع بعضها البعض. بينما يعمل أحد المطورين على الشاشة ، يمكن لآخر إنشاء ViewModel ، ويمكن لثالث كتابة الاستعلامات في المستودع.
- MVVM له تأثير إيجابي على اختبارات وحدة الكتابة. هذا البند يتبع من السابق. إذا تم تغليف جميع الفئات والأساليب من العمل مع واجهة المستخدم ، يمكن بسهولة اختبارها.
- حل طبيعي مع دوران الشاشة. لا يهم كم قد يبدو غريباً ، ولكن يتم الحصول على هذه الميزة تلقائيًا ، مع الانتقال إلى MVVM (لأنه يتم تخزين البيانات في ViewModel). إذا قمت بفحص التطبيقات الشائعة جدًا (VK و Telegram و Sberbank-Online و Aviasales) ، فقد تبين أن نصفها بالضبط لا يستطيعون تدوير الشاشة. مما يسبب لي بعض المفاجأة وسوء الفهم كمستخدم لهذه التطبيقات.
لماذا MVVM خطير؟- تسرب الذاكرة. يحدث هذا الخطأ الخطير إذا انتهكت قوانين استخدام LiveData والمراقب. سنبحث هذا الخطأ بالتفصيل في قسم الممارسة.
- المترامية الأطراف ViewModel. إذا حاولت احتواء كل منطق الأعمال في ViewModel ، فستحصل على رمز غير قابل للقراءة. قد يكون الخروج من هذا الموقف هو تقسيم ViewModel إلى تسلسل هرمي أو استخدام Presenters. هذا هو بالضبط ما فعلته.
قواعد للعمل مع MVVMلنبدأ بأكثر الأخطاء وأذهب إلى الأخطاء الأقل:
- يجب ألا يكون نص الطلب في ViewModel (فقط في المستودع) ؛
- يتم تعريف كائنات LiveData في ViewModel ، فهي لا تلقي نفسها داخل المستودع ، لأن تتم معالجة الطلبات في المستودع باستخدام Rx-Java (أو coroutines) ؛
- يجب نقل جميع وظائف المعالجة إلى فئات وملفات الجهات الخارجية ("مقدمو العروض") ، حتى لا تشوش ViewModel وعدم صرف الانتباه عن الجوهر.
LiveData
LiveData هي فئة حامل بيانات يمكن ملاحظتها. بخلاف ما يمكن ملاحظته بشكل منتظم ، فإن LiveData تدرك دورة الحياة ، مما يعني أنها تحترم دورة حياة مكونات التطبيق الأخرى ، مثل الأنشطة أو الأجزاء أو الخدمات. يضمن هذا الوعي LiveData فقط بتحديث مراقبي مكون التطبيق في حالة دورة حياة نشطة.
المصدر: developer.android.com/topic/libraries/architecture/livedataيمكن استخلاص استنتاج بسيط من التعريف: LiveData هي أداة برمجة تفاعلية موثوقة. سنستخدمه لتحديث جزء واجهة المستخدم دون ربط البيانات. لماذا هذا
لا تسمح بنية ملفات XML بتوزيع موجز للبيانات التي تم الحصول عليها من <data> ... </data>. إذا كان كل شيء واضحًا مع الملفات الصغيرة ، فماذا عن الملفات الكبيرة؟ ماذا تفعل مع الشاشات المعقدة ، وتشمل عدة ويمر حقول متعددة؟ استخدام النماذج في كل مكان؟ الحصول على الارتباطات الميدانية شديدة؟ وإذا كان يجب تنسيق الحقل ، فاستعن بأساليب من حزم Java؟ هذا يجعل الكود بشكل يائس ومعكرونة بالكامل. ليس على الإطلاق ما وعد MVVM.
سيؤدي رفض ربط البيانات إلى إجراء تغييرات على جزء واجهة المستخدم بشفافية. ستحدث جميع التحديثات مباشرة داخل المراقب. لأن نظرًا لأن شفرة Kolin موجزة وواضحة ، فلن نواجه مشكلات مع المراقب المتضخم. سوف تصبح كتابة التعليمات البرمجية والحفاظ عليها أسهل. سيتم استخدام ملفات XML فقط للتصميم - لا توجد خاصية بالداخل.
ربط البيانات هو أداة قوية. يعد هذا الأمر رائعًا لحل بعض المشكلات ، ويتوافق بشكل جيد مع Java ، ولكن مع Kotlin ... مع Kotlin ، في معظم الحالات ، يكون ربط البيانات مجرد بدائي. ربط البيانات فقط يعقد الكود ولا يعطي أي مزايا تنافسية.
في Java ، كان لديك خيار: إما استخدام ربط البيانات ، أو كتابة الكثير من التعليمات البرمجية القبيحة. في Kotlin ، يمكنك الوصول إلى عناصر العرض مباشرة ، وتجاوز findViewById () ، وكذلك ممتلكاتها. على سبيل المثال:
يطرح سؤال منطقي: لماذا تهتم بنماذج البستنة داخل ملفات XML ، واستدعاء أساليب Java في ملفات XML ، مما يؤدي إلى تحميل منطق جزء XML إذا كان بالإمكان تجنب كل هذا؟
Coroutines بدلاً من Thread () و Rx-Java
Coroutines هي خفيفة الوزن بشكل لا يصدق وسهلة الاستخدام. إنها مثالية لأغلب المهام غير المتزامنة البسيطة: معالجة نتائج الاستعلام ، وتحديث واجهة المستخدم ، إلخ.
يمكن أن يحل Coroutines محل مؤشر الترابط () و Rx-Java بشكل فعال في الحالات التي لا يكون فيها الأداء العالي مطلوبًا ، لأن يدفعون مقابل الخفة مع السرعة. Rx-Java ، بلا شك ، أكثر وظيفية ، ولكن بالنسبة للمهام البسيطة ، فإن جميع أصولها ليست مطلوبة.
مايكروسوفت والباقي
للعمل مع خدمات Outlook ، سيتم استخدام Microsoft Graph API. باستخدام الأذونات المناسبة ، يمكنك من خلالها الحصول على جميع المعلومات اللازمة عن الموظفين والغرف والمؤتمرات (الاجتماعات). للتعرف على الوجوه ، سيتم استخدام الخدمة السحابية لـ Microsoft Face API.
بالنظر إلى الأمام قليلاً ، سأقول أنه لحل مشكلة قابلية التوسع ، تم استخدام التخزين السحابي Firebase. هذا سوف يناقش أدناه.
هندسة معمارية
مشاكل قابلية التوسع
من الصعب جداً جعل النظام قابلاً للتطوير كليًا أو جزئيًا. يصعب القيام بذلك بشكل خاص إذا لم يكن الإصدار الأول من التطبيق قابلاً للتطوير ، ويجب أن يصبح الإصدار الثاني. أرسل التطبيق v1 الطلبات إلى جميع الغرف في وقت واحد. ترسل كل من الأجهزة اللوحية بانتظام طلبات إلى الخادم لتحديث جميع البيانات. في الوقت نفسه ، لم تتزامن الأجهزة مع بعضها البعض ، لأنه المشروع ببساطة لا يحتوي على خادم خاص به.
بالطبع ، إذا تابعنا نفس المسار وأرسلنا طلبات N من كل من الأجهزة اللوحية N ، فإننا في مرحلة ما سنقوم بإلغاء Microsoft Graph API أو تجميد نظامنا.
سيكون من المنطقي استخدام حل خادم عميل يقوم فيه الخادم باستقصاء الرسم البياني ، وتجميع البيانات ، وعند الطلب ، يقدم المعلومات إلى الأجهزة اللوحية ، ولكن هنا نلتقي بالواقع. يتكون فريق المشروع من شخصين (مطور ومصمم Android). يحتاجون إلى الوفاء بالموعد النهائي وهو 7 أسابيع ولم يتم توفير الواجهة الخلفية ، لأن التحجيم هو مطلب من المطور. ولكن هذا لا يعني أنه يجب التخلي عن الفكرة؟
ربما يكون الحل الصحيح الوحيد في هذه الحالة هو استخدام التخزين السحابي. سيقوم Firebase باستبدال الخادم وسيكون بمثابة مخزن مؤقت. ثم اتضح ما يلي: يقوم
كل جهاز لوحي باستقصاء عنوانه فقط من Microsoft Graph API ، وإذا لزم الأمر ، يقوم بمزامنة البيانات في وحدة التخزين السحابية ، حيث يمكن قراءتها بواسطة أجهزة أخرى.ستكون ميزة هذا التنفيذ استجابة سريعة ، لأن Firebase يعمل في الوضع في الوقت الحقيقي. سنقوم بتقليل عدد الطلبات المرسلة إلى خادم N مرة ، مما يعني أن الجهاز سيعمل على البطارية لفترة أطول قليلاً. من وجهة النظر المالية ، فإن المشروع لم يرتفع في الأسعار ، لأنه بالنسبة إلى هذا المشروع ، تكفي النسخة المجانية من Firebase بوجود احتياطيات متعددة: سعة تخزينية تبلغ 1 جيجابايت و 10 آلاف ترخيص شهريًا و 100 اتصال في المرة الواحدة. يمكن أن تتضمن العيوب الاعتماد على إطار طرف ثالث ، ولكن Firebase يلهم الثقة فينا ، لأن إنه منتج مستقر يتم صيانته وتطويره بواسطة Google.
كانت الفكرة العامة للنظام الجديد كما يلي: أقراص N و منصة سحابة لمزامنة البيانات في الوقت الفعلي. لنبدأ تصميم التطبيق نفسه.
LiveData في المستودع
يبدو أنني وضعت مؤخرا قواعد جيدة الشكل وانتهكت على الفور واحد منهم. على عكس الاستخدام الموصى به لـ LiveData داخل ViewModel ، في هذا المشروع ، تتم تهيئة كائنات LiveData في المستودع ، ويتم الإعلان عن جميع المستودعات على أنها مفردة. لماذا هذا
ويرتبط حل مماثل مع وضع التطبيق. الأجهزة اللوحية مفتوحة من الساعة 8 صباحًا حتى 8 مساءً. كل هذا الوقت ، تم إطلاق مساعد غرفة الاجتماعات فقط عليها. نتيجةً لذلك ، يمكن أن تكون العديد من الكائنات طويلة الأمد (وهذا هو السبب في أن جميع المستودعات مصممة كـ مفردة).
في سياق العمل ، يتم تبديل محتوى واجهة المستخدم بانتظام ، مما يستلزم بدوره إنشاء واستجمام كائنات ViewModel. اتضح أنه إذا كنت تستخدم LiveData داخل ViewModel ، فسيتم إنشاء ViewModel لكل جزء تم إنشاؤه باستخدام مجموعة من كائنات LiveData المحددة. إذا تم عرض شظتين متشابهين في وقت واحد على الشاشة ، مع ViewModel مختلف ونموذج Base-ViewModel شائع ، فسيكون هناك تكرار لكائنات LiveData من Base-ViewModel أثناء التهيئة. في المستقبل ، سوف تشغل هذه التكرارات مساحة الذاكرة حتى يتم تدميرها بواسطة "أداة تجميع البيانات المهملة". لأن إذا كان لدينا بالفعل مستودع في شكل مفرد ونريد تقليل تكلفة إعادة إنشاء الشاشات ، فسيكون من الحكمة نقل كائنات LiveData إلى مستودع مفرد ، وبالتالي تسهيل كائنات ViewModel وتسريع التطبيق.
بالطبع ، هذا لا يعني أنك بحاجة إلى نقل جميع LiveData من ViewModel إلى المستودع ، ولكن يجب عليك أن تتعامل مع هذه المشكلة بشكل أكثر تفكيرًا وتقرر اختيارك بوعي. عيب هذا النهج هو زيادة في عدد الأشياء طويلة الأجل ، لأنه يتم تعريف كل المستودعات على أنها مفردة وكل واحد منهم يخزن كائنات LiveData. ولكن في حالة معينة ، ليس مساعد غرفة الاجتماعات ناقصًا ، لأن يعمل التطبيق بدون توقف طوال اليوم ، دون تبديل السياق إلى التطبيقات الأخرى.
العمارة الناتجة

- يتم تنفيذ جميع الطلبات في المستودعات. تم تصميم كل المستودعات (في غرفة الاجتماعات Helper هناك 11 منها) بشكل منفرد. وهي مقسمة حسب نوع الأشياء المعادة والمخفية وراء الواجهات.
- يوجد منطق الأعمال في ViewModel. بفضل استخدام "Presenters" ، تبين أن الحجم الكلي لجميع ViewModel (هناك 6 في المشروع) أقل من 120 سطرًا.
- النشاط والجزء لا يشاركان إلا في تغيير جزء واجهة المستخدم ، وذلك باستخدام المراقب و LiveData المرتجعتين من ViewModel.
- يتم تخزين وظائف معالجة البيانات وتوليدها في "مقدم". تستخدم بنشاط وظائف إذن من Kotlin لمعالجة البيانات.
تم نقل منطق الخلفية إلى Intent-Service:
- حدث تحديث الخدمة. الخدمة المسؤولة عن مزامنة بيانات الغرفة الحالية في Firebase و Graph API.
- المستخدم التعرف على الخدمة. يعمل فقط على الكمبيوتر اللوحي الرئيسي. مسؤول عن إضافة موظفين جدد للنظام. يتحقق من قائمة الأشخاص المدربين بالفعل مع قائمة من Active Directory. إذا ظهر أشخاص جدد ، فإن الخدمة تضيفهم إلى واجهة برمجة تطبيقات Face وإعادة تدريب الشبكة العصبية. عند الانتهاء من العملية ، يتم إيقاف تشغيله. يبدأ عند بدء تشغيل التطبيق.
- تقوم خدمة الإعلام عبر الإنترنت بإخطار الأجهزة اللوحية الأخرى بأن هذا الجهاز اللوحي يعمل ، أي البطارية الخارجية ليست مستنفدة. وهو يعمل من خلال Firebase.
وكانت النتيجة بنية مرنة وصحيحة من وجهة نظر توزيع المسؤوليات التي تلبي جميع متطلبات التنمية الحديثة. إذا تخلينا في المستقبل عن Microsoft Graph API أو Firebase أو أي وحدة نمطية أخرى ، فيمكن استبدالها بسهولة بوحدات جديدة دون التدخل في بقية التطبيق. أتاح وجود نظام مكثف من "مقدمي العروض" اتخاذ جميع وظائف معالجة البيانات خارج الأساسية. ونتيجة لذلك ، أصبحت الهندسة المعمارية واضحة تمامًا ، وهي ميزة كبيرة. اختفت مشكلة ViewModel المتضخمة تمامًا.
أدناه ، سأقدم مثالًا على الحزمة الشائعة الاستخدام في تطبيق مطور.
الممارسة. مشاهدة التحديثات
حسب حالة غرفة الاجتماعات ، يعرض الاتصال الهاتفي أحد الشروط التالية:


بالإضافة إلى ذلك ، توجد أقواس مؤقتة من التجمعات على طول المخطط الهاتفي ، ويقوم المركز بالعد التنازلي حتى نهاية الاجتماع أو حتى بداية التجمع التالي. كل هذا يتم بواسطة مكتبة اللوحات التي طورناها. إذا تغيرت شبكة الاجتماعات ، يجب علينا تحديث البيانات في المكتبة.
نظرًا لأن LiveData يتم الإعلان عنها في المستودعات ، فمن المنطقي أن نبدأ بها.
مستودعات
FirebaseRoomRepository - فئة مسؤولة عن إرسال ومعالجة الطلبات في Firebase المتعلقة بنموذج الغرفة.
للتوضيح ، تم تبسيط رمز تهيئة firebase المستمع قليلاً (تمت إزالة وظيفة إعادة الاتصال). دعنا نلقي نظرة على نقاط ما يحدث هنا:
- تم تصميم المستودع على شكل حرف مفرد (في Kotlin ، يكفي استبدال الكلمة الأساسية للفئة بالكائن) ؛
- تهيئة كائنات LiveData ؛
- تم إعلان ValueEventListener كمتغير من أجل تجنب إعادة إنشاء فئة مجهولة في حالة إعادة الاتصال (تذكر ، قمت بتبسيط التهيئة عن طريق إزالة إعادة الاتصال في حالة انقطاع الاتصال) ؛
- تهيئة ValueEventListener (إذا تغيرت البيانات الموجودة في Firebase ، فسيقوم المستمع على الفور بتنفيذ وتحديث البيانات في كائنات LiveData) ؛
- تحديثات لكائنات LiveData.
يتم نقل الوظائف نفسها إلى ملف FirebaseRoomRepositoryPresenter منفصل وتزيينها كوظائف ملحق.
fun MutableLiveData<List<Room>>.updateOtherRooms(rooms: MutableList<Room>) { this.postValue(rooms.filter { !it.isOwnRoom() }) }
مثال على امتداد الوظيفة من FirebaseRoomRepositoryPresenterأيضًا لفهم عام للصورة ، سأقدم لك قائمة بكائن الغرفة.
- فئة البيانات. يقوم هذا المعدل تلقائيًا بإنشاء وتجاوز الأساليب toString () و HashCode () و (). لم تعد هناك حاجة لإعادة تعريفهم بنفسك.
- قائمة الأحداث من كائن الغرفة. هذه هي القائمة المطلوبة لتحديث البيانات في مكتبة الطلب.
جميع فئات المستودعات مخفية خلف فئة الواجهة.
object Repository {
- أعلاه يمكنك رؤية قائمة بجميع فئات المستودعات المستخدمة وواجهات المستوى الثاني. هذا يبسط الفهم العام للكود ويوضح قائمة بجميع فئات المستودعات المتصلة.
- قائمة الأساليب التي ترجع مراجع إلى كائنات LiveData من FirebaseRoomRepository. المستوطنين والأعراس في Kotlin اختيارية ، لذلك لا تحتاج إلى كتابتها دون داع.
تسمح لك هذه المنظمة بتلائم ما بين 20 إلى 30 طلبًا في مستودع جذر واحد بشكل مريح. إذا كان التطبيق الخاص بك يحتوي على المزيد من الطلبات ، فسوف يتعين عليك تقسيم واجهة الجذر إلى 2 أو أكثر.
ViewModel
BaseViewModel هو ViewModel الأساسي الذي يتم من خلاله توريث جميع ViewModels. ويتضمن كائنًا واحدًا للتيار المستمر ، يُستخدم عالميًا.
- تعني العلامة المفتوحة أنه يمكنك أن ترث من الفصل. بشكل افتراضي في Kotlin ، تكون جميع الفئات والأساليب نهائية ، أي لا يمكن توريث الفئات ، ولا يمكن إعادة تعريف الطرق. هذا لحماية ضد التغييرات إصدار غير المتوافقة عرضي. سأقدم مثالا.
أنت تقوم بتطوير نسخة جديدة من المكتبة. في مرحلة ما ، لسبب أو لآخر ، قررت إعادة تسمية الفئة أو تغيير توقيع بعض الطرق. عن طريق تغييره ، قمت بإنشاء خطأ عدم توافق الإصدار. عفوًا ... إذا كنت على الأرجح تعلم أن الطريقة قد يتم تجاوزها من قِبل شخص ما ، وقد تكون الفئة موروثة ، فمن المحتمل أن تكون أكثر دقة ومن الصعب أن تطلق النار على قدمك. للقيام بذلك ، في Kotlin ، افتراضيًا ، يتم إعلان كل شيء على أنه نهائي ، وللإلغاء ، هناك معدل "مفتوح".
- الأسلوب getCurrentRoom () بإرجاع ارتباط إلى كائن LiveData من الغرفة الحالية من السجل ، والذي بدوره ، مأخوذ من FirebaseRoomRepository. عند استدعاء هذه الطريقة ، سيعود كائن الغرفة الذي يحتوي على جميع المعلومات حول الغرفة ، بما في ذلك قائمة الأحداث.
لتحويل البيانات من تنسيق إلى آخر ، سنستخدم التحويل. للقيام بذلك ، قم بإنشاء
MainFragmentViewModel وارثه من
BaseViewModel .
MainFragmentViewModel هي فئة
مشتقة من BaseViewModel. يتم استخدام ViewModel هذا فقط في MainFragment.
- لاحظ عدم وجود معدل مفتوح. هذا يعني أن لا أحد يرث من الفصل.
- currentRoomEvents - كائن تم الحصول عليه باستخدام التحويل. بمجرد أن يتغير كائن الغرفة الحالية ، يتم إجراء التحويل ويتم تحديث كائن currentRoomEvents.
- MediatorLiveData. النتيجة مطابقة للتحول (كما هو موضح للمرجع).
يتم استخدام الخيار الأول لتحويل البيانات من نوع إلى آخر ، وهو ما نحتاجه ، والخيار الثاني مطلوب لتنفيذ بعض منطق الأعمال. ومع ذلك ، لا يحدث تحويل البيانات. تذكر أن استيراد android في ViewModel غير صالح. لذلك ، أبدأ طلبات إضافية من هنا أو أعد تشغيل الخدمات حسب الضرورة.
إشعار مهم! من أجل أن يعمل التحول أو الوسيط ، يجب أن يشترك شخص ما فيه من جزء أو نشاط. خلاف ذلك ، لن يتم تنفيذ الكود ، لأن لن يتوقع أحد نتيجة (هذه كائنات مراقبة).
MainFragment
الخطوة الأخيرة في تحويل البيانات إلى نتيجة. MainFragment يتضمن مكتبة الاتصال الهاتفي و View-Pager في أسفل الشاشة.
class MainFragment : BaseFragment() {
- تهيئة MainFragmentViewModel. يشير معدّل المتأخر إلى أننا نعد بتهيئة هذا الكائن لاحقًا ، قبل استخدامه. يحاول Kotlin حماية المبرمج من الكتابة غير الصحيحة للكود ، لذلك يجب أن نقول على الفور أن الكائن يمكن أن يكون لاغًا أو وضعه في وقت متأخر. في هذه الحالة ، يجب تهيئة ViewModel بواسطة الكائن.
- مراقب المستمع لتحديث الطلب.
- تهيئة ViewModel. يرجى ملاحظة أن هذا يحدث فور إرفاق الجزء بالنشاط.
- بعد إنشاء النشاط ، نحن نشترك في إجراء تغييرات على كائن currentRoomEvents. يرجى ملاحظة أنني لا أشترك في دورة حياة الجزء (هذا) ، لكن مع كائن viewLifecycleOwner. الحقيقة هي أنه في مكتبة الدعم 28.0.0 و AndroidX 1.0.0 تم اكتشاف خطأ عندما تم إلغاء اشتراك المراقب. لحل هذه المشكلة ، تم إصدار تصحيح في شكل viewLifecycleOwner ، وتوصي Google بالاشتراك فيه. هذا يحل مشكلة مراقب الزومبي عندما ماتت الشظية ويستمر المراقب في العمل. إذا كنت لا تزال تستخدم هذا ، فتأكد من استبداله بـ viewLifecycleOwner.
وبالتالي ، أود أن أظهر بساطة وجمال MVVM و LiveData دون استخدام ربط البيانات. يرجى ملاحظة أنني في هذا المشروع انتهكت القاعدة المقبولة عمومًا عن طريق وضع LiveData في المستودع بسبب تفاصيل المشروع. ومع ذلك ، إذا نقلناهم إلى ViewModel ، فستبقى الصورة العامة كما هي.
ككرز على كعكة ، أعددت لك مقطع فيديو قصيرًا مع عرض توضيحي (يتم تشويه الأسماء وفقًا لمتطلبات الأمان ، أعتذر):

النتائج
نتيجة لعمل التطبيق في الشهر الأول ، تم الكشف عن بعض الأخطاء في عرض التجمعات المتقاطعة (يسمح لك Outlook بإنشاء العديد من الأحداث في نفس الوقت ، بينما لا يقوم نظامنا بذلك). الآن النظام يعمل لمدة 3 أشهر. لا يتم ملاحظة الأخطاء أو الفشل.
PS شكرا
jericho_code للتعليق. في Kotlin ، يمكنك ويجب عليك تهيئة القائمة <> في النموذج باستخدام blankList () ، ثم لا يتم إنشاء كائن إضافي.
var events: List<Event.Short> = emptyList()