كيف قمنا بتنفيذ التنقل من Jetpack إلى تطبيق قتالي. تقرير ياندكس

تستخدم التطبيقات النقالة بشكل متزايد روابط عميقة. هذه هي الروابط التي تتيح لك ليس فقط الذهاب إلى التطبيق من الخارج ، ولكن للوصول إلى شاشة محددة. أوضح Vladislav Kozhushko ، وهو مطور يعمل بنظام Android من Yandex.Food ، سبب تطبيقنا للتنقل من Jetpack لتنفيذ الروابط العميقة ، والمشاكل التي واجهناها ، وكيف تم حلها وما حدث في النهاية.


- مرحبا بالجميع! اسمي فلاد. لقد كنت مهتمًا بتطوير Android منذ عام 2013 ، لقد كنت أعمل في Yandex.Ed منذ الصيف الماضي. سأخبرك عن طريقتنا في تقديم مكتبة "مكونات التنقل" إلى تطبيق قتالي.

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

وهنا اعتقدنا - التنقل المقدم على Google I / O 2018 مناسب تمامًا لتنفيذ المهام على deeplinks. نقرر أن نرى ما يحدث. لدى زملائنا الذين يستخدمون iOS في Xcode محرر رسومي مناسب يمكنهم فيه استخدام الماوس لكزة تخطيط الشاشات بالكامل ، بالإضافة إلى تعيين انتقالات بين الشاشات. الآن لدينا هذه الفرصة أيضًا ، يمكننا استخدام الماوس لضبط التحولات وإلغاء ربطها على الشاشات وربطها بالشظايا. بالإضافة إلى ذلك ، يمكننا ضبط الوسائط على الشاشة.



بمعنى ، يجب أن تكون الوسائط متوقفة في محرر واجهة المستخدم ، أو تكون مكتوبة بلغة XML. بالإضافة إلى التبديل بين الشاشات ، ظهرت علامة إجراء توضح فيها هويتها والشاشة التي نحتاج إلى الانتقال إليها.



لقد أشرنا أيضًا إلى deeplink الذي نريد فتح الشاشة به. في هذه الحالة ، هناك معلمة itemId ، إذا مررنا معلمة من هذا النوع ، وسوف تؤدي إلى جزء ، ثم سيتم تمرير قيمة هذه المعلمة إلى وسيطات التجزئة ، ويمكننا الحصول عليها واستخدامها باستخدام مفتاح itemId. تدعم المكتبة أيضًا الوصلات الصاعدة. إذا قمنا بتعيين الوصلة الصاعدة ، على سبيل المثال ، navdemo.ru/start/{itemId} ، فلن نحتاج بعد الآن إلى القلق بشأن تسجيل مخططات http / https لفتح مثل هذه الدبلومات. المكتبة سوف تفعل كل شيء لنا.

الآن دعونا نتحدث عن الحجج. أضفنا وسيطين ، عدد صحيح وبولي ، وأعطيناهم أيضًا قيمًا افتراضية.



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



حول الميزات الرئيسية للمكتبة. لديها محرر واجهة مستخدم مناسب يمكننا من خلاله القيام بكل عمليات التنقل. نحصل على علامة XML قابلة للقراءة تتضخم بنفس طريقة العرض طرق العرض. وليس لدينا بعض الآلام التي فعلها مطورو iOS عندما قاموا بتصحيح شيء ما في المحرر الرسومي ، وتغيير الكثير من الأشياء.

يمكن أن تعمل Deeplinks مع الشظايا ومع النشاط ، ولهذا لا تحتاج إلى تسجيل كمية كبيرة من IntentFilter في البيان. نحن ندعم أيضًا الوصلات الصاعدة ، ومع Android 6 يمكنك تمكين التحقق التلقائي للتحقق. بالإضافة إلى ذلك ، عند تجميع المشروعات ، يتم إنشاء الكود بالوسيطات على الشاشة المطلوبة. يدعم التنقل الرسوم البيانية المتداخلة والتنقل المتداخل ، والذي يسمح لك بتحليل التنقل بأكمله منطقياً إلى مكونات فرعية منفصلة.



الآن سنتحدث عن طريقنا ، الذي مررنا به ، من خلال تقديم المكتبة. لقد بدأ كل شيء بالإصدار ألفا 3. قمنا بتنفيذ كل شيء ، واستبدلنا كل التنقل بمكونات التنقل ، كل شيء رائع ، كل شيء يعمل ، الدبلومات مفتوحة ، ولكن ظهرت مشاكل.



المشكلة الأولى هي IllegalArgumentException. لقد ظهر في حالتين: عدم تناسق الرسم البياني ، لأنه كان هناك عدم التزامن لتمثيل الرسم البياني والشظايا على المكدس ، ولهذا السبب كان هناك استثناء. المشكلة الثانية هي نقرات مزدوجة. عندما قمنا بإجراء النقرة الأولى ، فإننا نتنقل. ينتقل إلى الشاشة التالية ، وتتغير حالة الرسم البياني. عندما ننفذ النقرة الثانية ، يكون الرسم البياني في حالة جديدة بالفعل ، ويحاول إجراء الانتقال القديم ، الذي لم يعد موجودًا ، لذلك نحصل على مثل هذا الاستثناء. في هذا الإصدار ، لم يتم فتح الدبلومات ، حيث يحتوي مخططها على نقطة ، على سبيل المثال ، project.company. تم تحديد ذلك في الإصدارات المستقبلية من المكتبة ، وفي الإصدار الثابت ، كل شيء يعمل بشكل جيد.

أيضا ، العناصر المشتركة غير مدعمة. من المحتمل أنك رأيت كيفية عمل Google Play: هناك قائمة بالتطبيقات ، والنقر فوق التطبيق ، وفتح الشاشة ، وتحدث رسوم متحركة جميلة لتحريك الأيقونة. لدينا أيضًا هذا في التطبيق على قائمة المطاعم ، لكننا كنا بحاجة إلى دعم العناصر المشتركة. أيضا ، SafeArgs لم تنجح بالنسبة لنا ، لذلك عشنا بدونهم.



كان من السهل إصلاح deeplink. كان من الضروري استبدال المخطط الذي كان في المكتبة بمخططه الخاص ، والذي يدعم هذه النقطة. بمساعدة الانعكاس ، نطرق الفصل ونغير قيمة regex ويعمل كل شيء.



لتصحيح النقر المزدوج ، استخدمنا الطريقة التالية. لدينا وظائف التمديد لضبط النقرات في التنقل. بعد النقر فوق زر أو عنصر آخر ، نقوم بتحديث ClickListener وننفذ التنقل لتجنب انتقال مزدوج. أو إذا كان لديك RxJava في مشروعك ، فإنني أوصي باستخدام مكتبة RxBindingsgs من Jake Worton ، ومعها يمكنك التعامل مع الأحداث من View بأسلوب تفاعلي ، باستخدام العوامل المتاحة لنا.



دعنا نتحدث عن العناصر المشتركة. نظرًا لظهورها بعد ذلك بقليل ، فقد قررنا إنهاء الملاح ، وإضافة التنقل إليه. نحن مبرمجين ، لم لا؟



كان التحسين على النحو التالي: لقد ورثنا المستكشف الخاص بنا من المستكشف الموجود في المكتبة. لا يتم تقديم كل الشفرة هنا ، ولكن هذا هو الجزء الرئيسي الذي وضعناه في صيغته النهائية. أريد أن أشير إلى أنه قبل القيام بالملاحة ، يتم التحقق من حالة FragmentManager. إذا تم حفظه ، فإننا نفقد فرقنا. في رأيي ، هذا عيب.

أيضًا ، عندما نبدأ معاملة جزئية ، فإننا ننشئ معاملة ونعين كل طرق العرض الخاصة بنا التي يجب البحث عنها. لكن السؤال هو ، أي نوع من الصفوف هو هذا الانتقال؟ هذه هي فصولنا المخصصة التي يمكن من خلالها تعيين طرق العرض. نرثها من الوجهة ونوسع الوظائف. وضعنا طرق العرض ووجهتنا جاهزة لمشاركتها.



الجزء التالي - نحن بحاجة لجعل الملاحة. عندما نضغط على الزر ، فإننا نبحث عن وجهة الهوية ، وننسحب الجزء العلوي من الرسم البياني الذي يجب أن نذهب إليه. بعد ذلك ، سوف نقوم بتحويله إلى TransitionDestination ، والذي لدينا آراء. بعد ذلك ، قمنا بتعيين كل وجهات نظرنا على تحريك التحولات ، وننفذ التنقل. كل شيء يعمل ، كل شيء رائع. ولكن بعد ذلك ظهرت alpha06.

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

كانت هناك مشاكل مع alpha06. نظرًا لأنه كان إصدارًا ألفا من المكتبة ، فقد كانت هناك تغييرات ثابتة مرتبطة بإعادة تسمية الأساليب وعمليات الاستدعاء والواجهات ، ناهيك عن حقيقة أنه تمت إضافة المعلمات وإزالتها في الطرق. نظرًا لأننا كتبنا متصفحنا الخاص ، فقد اضطررنا لمزامنة رمز متصفح المكتبة مع رمزنا من أجل ملء إصلاحات الأخطاء والميزات الجديدة.

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



على سبيل المثال ، كان هناك مثل هذا التغيير الذي قال فيه المطورون إن طريقة navigateUp () التي تعمل مع DrawerLayout قد تم إهمالها ، واستخدام آخر ، حيث يتم تبديل المعلمات ببساطة.





هجرتنا الكبيرة التالية كانت في alpha11. هنا إصلاح المشاكل الرئيسية مع الملاحة عند العمل مع الرسم البياني. أخيرًا أزلنا جهاز التحكم المنشط واستخدمنا كل ما كان خارج الصندوق. الحجج آمنة لا تزال لا تعمل بالنسبة لنا ، وكنا مستاء.

ثم تم إصدار beta01 ، وفي هذا الإصدار لم يتغير شيء تقريبًا أثناء سلوك التنقل ، ولكن ظهرت المشكلة التالية: إذا كان هناك عدد معين من الشاشات مفتوحة في التطبيق ، فسنقوم بمسح المكدس قبل فتح دبلومنا. هذا السلوك لم يناسبنا. الحجج آمنة لا تزال لا تعمل.



لقد كتبنا مشكلة على Google ، قيل لنا فيها أن جميع القواعد قد صُممت في الأصل ، وفي الكود نفسه كان هذا لأنه قبل التبديل إلى deeplink ، عدنا إلى جزء الجذر باستخدام معرف الرسم البياني الذي يكمن في الجذر. وأيضًا في طريقة setPopUpTo () يتم تمرير العلامة الحقيقية ، قائلة إنه بعد الرجوع إلى هذه الشاشة ، نحتاج أيضًا إلى إسقاطها من المكدس.



قررنا إعادة المستكشف المنشور وإصلاح ما نعتقد أنه خاطئ.



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

وأخيرا ، إصدار مستقر يخرج. كنا سعداء ، لقد اعتقدنا أن كل شيء على ما يرام ، ولكن في الإصدار الثابت ، لم يتغير السلوك إلى حد كبير. أنها مجرد جعله وضع اللمسات الأخيرة. لقد نجحنا أخيرًا في تشغيل وسيطات آمنة ، لذلك بدأنا في إضافة وسائط بشكل نشط على شاشاتنا واستخدامها في كل مكان في الكود. وجدنا أيضًا أن التنقل لا يعمل مع DialogFragments. نظرًا لوجود DialogFragments ، فقد أردنا نقلها جميعًا إلى رسم بياني بتنسيق XML ووصف التحولات بينها. لكننا لم ننجح.



فتح مزدوج. لقد واجهنا أيضًا مشكلة تطاردنا منذ الإصدار الأول - الافتتاح المزدوج للنشاط أثناء بداية التطبيق الباردة.



يحدث على النحو التالي. هناك طريقة رائعة لإلغاء اتصال deeplink يمكننا الاتصال بها من خلال الكود الخاص بنا ، على سبيل المثال ، عندما نبدأ في نشاطنا على newIntent () من أجل اعتراض رابط إلغاء الربط. هنا ، تأتي إشارة ACTIVITY_NEW_TASK إلى الهدف عندما يتم تشغيل التطبيق باستخدام deeplink ، وبالتالي يحدث ما يلي: يبدأ نشاط جديد ، وإذا كان هناك نشاط حالي ، فسيتم قتله. لذلك ، إذا أطلقنا التطبيق ، تبدأ الشاشة البيضاء أولاً ، ثم تختفي ، تظهر شاشة أخرى ، وتبدو جميلة جدًا.

نتيجة لذلك ، نقدم هذه المكتبة ، حصلنا على المزايا التالية.



لدينا وثائق للتنقل لدينا ، بالإضافة إلى تمثيل رسومي للتحولات بين الشاشات ، وإذا أتى إلينا شخص ما في مشروع ، فسرعان ما يفهم ذلك من خلال النظر إلى الرسم البياني ، وفتح عرضه التقديمي في الاستوديو.

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

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

يمكننا أيضًا تغيير الرسم البياني ديناميكيًا في الشفرة ، ويمكننا إضافة رؤوس ، ويمكننا حذف الرؤوس ، ويمكننا تغيير شاشة البدء - كل هذا يعمل.

لقد نسينا تقريبًا كيفية العمل مع FragmentManager ، نظرًا لأن كل منطق العمل به مغلف داخل المكتبة ، والمكتبة تقوم بكل السحر بالنسبة لنا. تعمل المكتبة أيضًا مع DrawerLayout ، إذا كان لديك مجموعة جزئية من الجذر ، فستقوم المكتبة نفسها برسم الهامبرغر ، وعند الانتقال إلى الشاشات التالية ، ستقوم برسم سهم وتقوم به بشكل متحرك عند العودة من الجزء قبل الأخير.

لقد قمنا أيضًا بتحويل جميع الحجج ، معظمها ، إلى SafeArgs ، ويتم إنشاء كل شيء عندما نبني المشروع. بالإضافة إلى ذلك ، تعاملنا مع جميع المشاكل التي ابتليت بها ، وقمنا بتعديل المكتبة لتناسب احتياجاتنا.

SafeArgs يمكن أن تولد كود Kotlin ، ويستخدم البرنامج المساعد منفصلة لهذا الغرض.



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

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

أيضًا ، يتم فقد أوامر النقل ، كل ذلك لأن جزء المستكشف يتحقق من الحالة سواء تم حفظه أم لا ، وإذا تم حفظه ، فلن نفعل شيئًا.

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

أيضا ، للعمل مع الملاحة ، يجب أن يكون لديك نسخة عرض. للحصول على المستكشف ، تحتاج إلى الانتقال إلى "العرض" - ستتصل مكتبة التنقل نفسها بآباء هذا العرض - وتحاول العثور على المستكشف فيه. إذا وجدت ذلك ، فإن الشاشة تذهب إلى الشاشة التي نحتاجها.

لكن السؤال الذي يطرح نفسه: هل يستحق استخدام المكتبة في المعركة؟ سأقول نعم إذا:



إذا كنا بحاجة للحصول على نتيجة سريعة. يتم تنفيذ المكتبة بسرعة كبيرة ، ويمكن إدراجها في المشروع في حوالي 20 دقيقة ، وجميع التحولات مطعون بالماوس ، كل هذا مناسب. كما يتم كتابتها بسرعة في XML ، وتجميعها بسرعة ، ويعمل بسرعة. كل شيء رائع.

إذا كنت بحاجة إلى العديد من الشاشات في التطبيق التي تعمل مع deeplinks ، ولا توجد شروط يجب فتحها.

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

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

أنت مستعد للدخول إلى المصدر وتعديله كما نفعل. هذا هو في الواقع مثيرة جدا للاهتمام.



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

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

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

شيء واحد أكثر أهمية - DialogFragments غير معتمد. يمكن إضافة الملاح الذي سيعمل معهم ، ولكن لسبب ما لم تتم إضافتهم. المشكلة هي أنهم يفتحون أنفسهم كأجزاء عادية. لم يتم تعيين خانة الاختيار لـ isShowing ، وبالتالي ، لا يتم تنفيذ دورة حياة DialogFragments لإنشاء مربع حوار. في الواقع ، يتم فتح DialogFragment كجزء عادي ، الشاشة بأكملها دون إنشاء نافذة.

هذا كل شيء بالنسبة لي. حفر المصدر ، وهذا مثير للاهتمام حقا ومثيرة. فيما يلي مواد مفيدة لأولئك الذين يرغبون في العمل مع مكتبة التنقل. شكرا لاهتمامكم

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


All Articles