تعمل مكتبة Dynamic Navigator على توسيع وظائف مكوّن Jetpack Navigation للعمل مع الوجهات المحدّدة في وحدات الميزات. توفر هذه المكتبة أيضًا تثبيتًا سلسًا لوحدات الميزات عند الطلب عند الانتقال إلى هذه الوجهات.
ضبط إعدادات
لإتاحة وحدات الميزات، استخدِم الاعتماديات التالية في ملف build.gradle
الخاص بوحدة تطبيقك:
رائع
dependencies { def nav_version = "2.7.7" api "androidx.navigation:navigation-fragment-ktx:$nav_version" api "androidx.navigation:navigation-ui-ktx:$nav_version" api "androidx.navigation:navigation-dynamic-features-fragment:$nav_version" }
Kotlin
dependencies { val nav_version = "2.7.7" api("androidx.navigation:navigation-fragment-ktx:$nav_version") api("androidx.navigation:navigation-ui-ktx:$nav_version") api("androidx.navigation:navigation-dynamic-features-fragment:$nav_version") }
لاحظ أنه يجب أن تستخدم تبعيات التنقل الأخرى إعدادات واجهة برمجة التطبيقات حتى تكون متاحة لوحدات الميزات لديك.
الاستخدام الأساسي
لإتاحة وحدات الميزات، عليك أولاً تغيير جميع مثيلات
NavHostFragment
في تطبيقك إلى
androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment
:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment"
app:navGraph="@navigation/nav_graph"
... />
بعد ذلك، أضِف السمة app:moduleName
إلى أي وجهات <activity>
أو <fragment>
أو
<navigation>
في الرسومات البيانية
للتنقّل في وحدة com.android.dynamic-feature
المرتبطة بـ DynamicNavHostFragment
.
تخبر هذه السمة مكتبة Dynamic Navigator بأن الوجهة
تنتمي إلى وحدة ميزة بالاسم الذي تحدده.
<fragment
app:moduleName="myDynamicFeature"
android:id="@+id/featureFragment"
android:name="com.google.android.samples.feature.FeatureFragment"
... />
عند الانتقال إلى إحدى هذه الوجهات، تتحقّق مكتبة Dynamic Navigator أولاً من تثبيت وحدة الميزة. إذا كانت وحدة الميزات موجودة بالفعل، ينتقل تطبيقك إلى الوجهة كما هو متوقع. في حال عدم توفّر الوحدة، يعرض تطبيقك وجهة متوسطة لجزء مستوى التقدّم أثناء تثبيت الوحدة. يعرض التنفيذ التلقائي لجزء التقدم واجهة مستخدم أساسية مع شريط تقدّم ويعالج أي أخطاء في التثبيت.
![شاشتا تحميل تعرضان واجهة المستخدم مع شريط تقدّم عند الانتقال
إلى وحدة من الميزات للمرة الأولى](https://developer.android.com/static/images/guide/navigation/dfm-nav-loading.png?authuser=7&hl=ar)
لتخصيص واجهة المستخدم هذه أو للتعامل مع مستوى تقدُّم عملية التثبيت يدويًا من داخل شاشة التطبيق، يمكنك الاطّلاع على القسمَين تخصيص جزء مستوى التقدّم ومراقبة حالة الطلب في هذا الموضوع.
إنّ الوجهات التي لا تحدّد app:moduleName
تستمر في العمل بدون
تغييرات وتبدو كما لو أنّ تطبيقك يستخدم NavHostFragment
عاديًا.
تخصيص جزء مستوى التقدّم
يمكنك إلغاء تنفيذ جزء التقدم لكل رسم بياني للتنقل
من خلال ضبط السمة app:progressDestination
على رقم تعريف الوجهة
التي تريد استخدامها للتعامل مع مستوى تقدُّم التثبيت. يجب أن تكون وجهة مستوى التقدّم المخصّص
Fragment
مشتقة من
AbstractProgressFragment
.
يجب إلغاء الطرق المجرّدة للإشعارات بشأن تقدم التثبيت والأخطاء والأحداث الأخرى. يمكنك بعد ذلك إظهار مستوى تقدم التثبيت في
واجهة مستخدم من اختيارك.
تستخدم فئة
DefaultProgressFragment
التنفيذ التلقائية واجهة برمجة التطبيقات هذه لعرض مستوى تقدُّم عملية التثبيت.
مراقبة حالة الطلب
تتيح لك مكتبة Dynamic Navigator تنفيذ مسار تجربة المستخدم على نحو مشابه للخطوات الواردة في أفضل ممارسات تجربة المستخدم للعرض عند الطلب، والذي يظل المستخدم فيه في سياق شاشة سابقة مع انتظار انتهاء التثبيت. وهذا يعني أنك لست بحاجة إلى عرض واجهة مستخدم متوسطة أو جزء التقدم على الإطلاق.
![شاشة تعرض شريط تنقل سفلي فيه رمز يشير إلى
تنزيل وحدة من الميزات](https://developer.android.com/static/images/guide/navigation/nav-dfm-ui.png?authuser=7&hl=ar)
في هذا السيناريو، تكون مسئولاً عن مراقبة ومعالجة جميع حالات التثبيت وتغييرات التقدم والأخطاء وما إلى ذلك.
لبدء مسار التنقّل الذي لا يؤدي إلى الحظر، مرِّر عنصر
DynamicExtras
يحتوي على
DynamicInstallMonitor
إلى
NavController.navigate()
،
على النحو الموضّح في المثال التالي:
Kotlin
val navController = ... val installMonitor = DynamicInstallMonitor() navController.navigate( destinationId, null, null, DynamicExtras(installMonitor) )
Java
NavController navController = ... DynamicInstallMonitor installMonitor = new DynamicInstallMonitor(); navController.navigate( destinationId, null, null, new DynamicExtras(installMonitor); )
وبعد استدعاء الدالة navigate()
مباشرةً، يجب التحقّق من قيمة
installMonitor.isInstallRequired
لمعرفة ما إذا كانت محاولة التنقّل قد أدّت إلى تثبيت وحدة الميزات.
- إذا كانت القيمة هي
false
، أنت تنتقل إلى وجهة عادية ولا تحتاج إلى اتخاذ أي إجراء آخر. إذا كانت القيمة هي
true
، عليك البدء بمراقبة كائنLiveData
الذي أصبح الآن فيinstallMonitor.status
. يُصدر عنصرLiveData
هذا تحديثاتSplitInstallSessionState
من مكتبة Play الأساسية. تحتوي هذه التحديثات على أحداث تقدم التثبيت التي يمكنك استخدامها لتحديث واجهة المستخدم تذكر التعامل مع جميع الحالات ذات الصلة على النحو الموضح في دليل Play الأساسي، بما في ذلك طلب تأكيد المستخدم إذا لزم الأمر.Kotlin
val navController = ... val installMonitor = DynamicInstallMonitor() navController.navigate( destinationId, null, null, DynamicExtras(installMonitor) ) if (installMonitor.isInstallRequired) { installMonitor.status.observe(this, object : Observer<SplitInstallSessionState> { override fun onChanged(sessionState: SplitInstallSessionState) { when (sessionState.status()) { SplitInstallSessionStatus.INSTALLED -> { // Call navigate again here or after user taps again in the UI: // navController.navigate(destinationId, destinationArgs, null, null) } SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> { SplitInstallManager.startConfirmationDialogForResult(...) } // Handle all remaining states: SplitInstallSessionStatus.FAILED -> {} SplitInstallSessionStatus.CANCELED -> {} } if (sessionState.hasTerminalStatus()) { installMonitor.status.removeObserver(this); } } }); }
Java
NavController navController = ... DynamicInstallMonitor installMonitor = new DynamicInstallMonitor(); navController.navigate( destinationId, null, null, new DynamicExtras(installMonitor); ) if (installMonitor.isInstallRequired()) { installMonitor.getStatus().observe(this, new Observer<SplitInstallSessionState>() { @Override public void onChanged(SplitInstallSessionState sessionState) { switch (sessionState.status()) { case SplitInstallSessionStatus.INSTALLED: // Call navigate again here or after user taps again in the UI: // navController.navigate(mDestinationId, mDestinationArgs, null, null); break; case SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION: SplitInstallManager.startConfirmationDialogForResult(...) break; // Handle all remaining states: case SplitInstallSessionStatus.FAILED: break; case SplitInstallSessionStatus.CANCELED: break; } if (sessionState.hasTerminalStatus()) { installMonitor.getStatus().removeObserver(this); } } }); }
عند انتهاء التثبيت، يصدر الكائن LiveData
حالة SplitInstallSessionStatus.INSTALLED
. عليك بعد ذلك الاتصال بـ
NavController.navigate()
مرة أخرى. نظرًا لأنه تم تثبيت الوحدة الآن، تنجح المكالمة
الآن، وينتقل التطبيق إلى الوجهة كما هو متوقع.
بعد الوصول إلى حالة طرفية، مثل اكتمال التثبيت أو عند تعذُّر التثبيت، يجب إزالة مراقب LiveData
لتجنُّب تسرب الذاكرة. يمكنك التحقّق مما إذا كانت الحالة تمثّل حالة طرفية باستخدام
SplitInstallSessionStatus.hasTerminalStatus()
.
يمكنك الاطّلاع على AbstractProgressFragment
للحصول على مثال لتنفيذ هذا المراقب.
الرسومات البيانية المضمّنة
تتيح مكتبة Dynamic Navigator تضمين رسوم بيانية محدّدة في وحدات الميزات. لتضمين رسم بياني محدد في وحدة ميزات، يمكنك إجراء ما يلي:
استخدِم
<include-dynamic/>
بدلاً من<include/>
، كما هو موضّح في المثال التالي:<include-dynamic android:id="@+id/includedGraph" app:moduleName="includedgraphfeature" app:graphResName="included_feature_nav" app:graphPackage="com.google.android.samples.dynamic_navigator.included_graph_feature" />
داخل
<include-dynamic ... />
، يجب تحديد السمات التالية:app:graphResName
: اسم ملف موارد الرسم البياني للتنقل والاسم مشتق من اسم ملف الرسم البياني. على سبيل المثال، إذا كان الرسم البياني فيres/navigation/nav_graph.xml
، يكون اسم المورد هوnav_graph
.android:id
- رقم تعريف وجهة الرسم البياني تتجاهل مكتبة Dynamic Navigator أي قيمandroid:id
في العنصر الجذر للرسم البياني المضمَّن.app:moduleName
: اسم حزمة الوحدة.
استخدام الشكل البياني الصحيح
من المهم ضبط app:graphPackage
بشكل صحيح لأن مكوِّن التنقل لن يتمكن من تضمين navGraph
المحددة من وحدة الميزات، وإلا.
يتم إنشاء اسم الحزمة لوحدة الميزات الديناميكية من خلال إلحاق
اسم الوحدة بـ applicationId
الخاصة بوحدة التطبيق الأساسية. مثلاً، إذا احتوت وحدة التطبيق الأساسية على applicationId
للخاصية com.example.dynamicfeatureapp
واسم وحدة الميزات الديناميكية DynamicFeatureModule
، سيكون اسم حزمة الوحدة الديناميكية هو com.example.dynamicfeatureapp.DynamicFeatureModule
. يكون اسم الحزمة هذا حساسًا
لحالة الأحرف.
إذا لم تكن متأكدًا، يمكنك التأكّد من اسم الحزمة لوحدة الميزات
من خلال الاطّلاع على سمة AndroidManifest.xml
التي تم إنشاؤها. بعد إنشاء المشروع، انتقل إلى
<DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml
، والذي يُفترض أن يبدو على النحو التالي:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:dist="http://schemas.android.com/apk/distribution" featureSplit="DynamicFeatureModule" package="com.example.dynamicfeatureapp" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" /> <dist:module dist:instant="false" dist:title="@string/title_dynamicfeaturemodule" > <dist:delivery> <dist:install-time /> </dist:delivery> <dist:fusing dist:include="true" /> </dist:module> <application /> </manifest>
يجب أن تتطابق قيمة featureSplit
مع اسم وحدة الميزات الديناميكية، وستطابق الحزمة قيمة applicationId
الخاصة بوحدة التطبيق الأساسية. تجمع السمة app:graphPackage
ما يلي: com.example.dynamicfeatureapp.DynamicFeatureModule
.
جارٍ الانتقال إلى رسم بياني مضمَّن للتنقّل الديناميكي.
لا يمكن التنقل إلا إلى startDestination
في
رسم بياني للتنقل في include-dynamic
. تكون الوحدة الديناميكية مسؤولة عن الرسم البياني
للتنقل الخاص بها وليس لدى التطبيق الأساسي معرفة بذلك.
تعمل آلية التضمين الديناميكية على تفعيل وحدة التطبيق الأساسية من تضمين
رسم بياني مدمج للتنقّل
يتم تحديده ضمن الوحدة الديناميكية. يعمل الرسم البياني المتداخل للتنقل هذا
مثل أي رسم بياني متداخل للتنقل. يمكن للرسم البياني للتنقل الجذر (أي مصدر الرسم البياني المتداخل) تحديد الرسم البياني للتنقل المتداخل نفسه كوجهة وليس عناصره الثانوية. وبالتالي، يتم استخدام startDestination
عندما يكون
الرسم البياني للتنقُّل الديناميكي هو الوجهة.
القيود
- لا تتوافق حاليًا الرسوم البيانية المضمّنة ديناميكيًا مع الروابط لصفحات في التطبيق.
- لا تتوافق حاليًا الرسوم البيانية المدمَجة التي تم تحميلها ديناميكيًا (أي عنصر
<navigation>
معapp:moduleName
) مع الروابط لصفحات في التطبيق.