نطير عبر الوحدات النمطية: التنقل في تطبيق متعدد الوحدات باستخدام Jetpack


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


الرسم البياني الملاحة


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



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


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


الحديث رخيص. أرني الرمز


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



الوصول إلى التنقل داخل الوحدة النمطية


عندما يحين الوقت لإجراء انتقال من شاشة إلى شاشة في وحدة نمطية أخرى ، يطرح السؤال - كيف؟


في الواقع ، لا يوجد داخل وحدة الميزات ميزة الوصول إلى الرسم البياني للتنقل للحصول على معرف الإجراء الذي يجب على NavController تنفيذه.


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


SplashNavCommandProvider.kt


interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

سيتم تطبيق واجهة موفر الأوامر نفسها في وحدة التطبيق ، وستحتوي فئة أوامر التنقل على نفس الحقول مثل الوسائط الخاصة بأسلوب NavController.navigate


NavCommand.kt


 data class NavCommand( val action: Int, var args: Bundle? = null, val navOptions: NavOptions? = null ) 

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


SplashNavCommandProvider.kt


 interface SplashNavCommandProvider { val toAuth: NavCommand val toMain: NavCommand } 

في وحدة التطبيق ، نقوم بإنشاء تطبيق لهذه الواجهة وباستخدام إطار di (لدي Dagger) ، نوفره من خلال واجهة البداية للوحدة.


SplashNavCommandProviderImpl.kt - تنفيذ CommandProvider


 class SplashNavCommandProviderImpl @Inject constructor() : SplashNavCommandProvider { override val toAuth: NavCommand = NavCommand(R.id.action_splashFragment_to_authFragment) override val toMain: NavCommand = NavCommand(R.id.action_splashFragment_to_mainFragment) } 

SplashNavigationModule.kt - وحدة DI لتوفير التبعيات


 @Module interface SplashNavigationModule { @Binds fun bindSplashNavigator(impl: SplashNavCommandProviderImpl): SplashNavCommandProvider } 

AppActivityModule.kt - الوحدة الرئيسية DI للتطبيق


 @Module interface AppActivityModule { @FragmentScope @ContributesAndroidInjector( modules = [ SplashNavigationModule::class ] ) fun splashFragmentInjector(): SplashFragment … } 

في وحدة البداية ، ننفذ التنفيذ في MV (هنا) إما مقدم العرض أو ViewModel ...


SplashViewModel.kt


 class SplashViewModel @Inject constructor( private val splashNavCommandProvider: SplashNavCommandProvider ) ... 

عندما يرى منطق الشاشة أن الوقت قد حان للتبديل إلى شاشة أخرى ، فإننا ننقل أمرًا إلى جزءنا ونعلم أننا بحاجة إلى التبديل إلى شاشة أخرى.


سيكون من الممكن تنفيذ تطبيق SplashNavCommandProvider مباشرة في الجزء ، ولكن بعد ذلك نفقد القدرة على اختبار الملاحة.


في الجزء نفسه ، لإكمال الانتقال ، تحتاج إلى الحصول على NavController. إذا لم تكن الشاشة الحالية جزءًا متداخلًا ، فسنحصل ببساطة على NavController باستخدام طريقة findNavController () ونستدعي طريقة التنقل عليها:


 findNavController().navigate(toMain) 

يمكنك جعله أكثر ملاءمةً قليلاً بكتابة ملحق للجزء


FragmentExt.kt


 fun Fragment.navigate(navCommand: NavCommand) { findNavController().navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

لماذا فقط لشظية؟ لأنني أستخدم نهج SingleActivity ، إذا كان لديك العديد منها ، فيمكنك أيضًا إنشاء ملحقات للنشاط.


ثم الملاحة داخل الجزء ستبدو هكذا


 navigate(toMain) 

شظايا متداخلة


يمكن أن يكون التنقل في الأجزاء المتداخلة من نوعين:


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

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


GlobalHostModule.kt - الوحدة النمطية DI لتوفير تبعيات معرف المضيف العالمي


 @Provides @GlobalHost fun provideGlobalHostId(): Int = R.id.host_global 

AppActivityModule.kt - الوحدة الرئيسية DI للتطبيق


 @FragmentScope @ContributesAndroidInjector( modules = [ GlobalHostModule::class, ProfileNavigationModule::class, ... ] ) fun profileKnownFragmentInjector(): ProfileKnownFragment 

تضمين تبعية معرف المضيف في جزء منه


 @Inject @GlobalHost var hostId = 0 

عندما تكون الأجزاء متداخلة ، فإن الأمر يستحق إنشاء Qualifier لكل مضيف أو استخدام Qualifier Named الموجود حتى يدرك Dagger ما هو المراد توفيره.


GlobalHost.kt


 @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class GlobalHost 

بعد الحصول على معرف التبعية للمضيف المطلوب في الجزء ، يمكنك الحصول على NavController بواسطة معرف المضيف. سنقوم بتحسين امتدادنا للقدرة على إجراء انتقالات في أي حاوية:


FragmentExt.kt


 fun Fragment.navigate(navCommand: NavCommand, hostId: Int? = null) { val navController = if (hostId == null) { findNavController() } else { Navigation.findNavController(requireActivity(), hostId) } navController.navigate(navCommand.action, navCommand.args, navCommand.navOptions) } 

رمز في مقتطف


 navigate(toAuth, hostId) 

كانت هذه أبرز نقاط التنقل باستخدام Jetpack في بنية متعددة الوحدات. إذا كان لديك أي أسئلة ، فسأكون سعيدًا بالإجابة عليها في التعليقات :)

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


All Articles