Библиотека Dynamic Navigator расширяет функциональность компонента Jetpack Navigation, позволяя работать с целевыми страницами, определенными в модулях функций. Эта библиотека также обеспечивает беспроблемную установку модулей функций по запросу при переходе к этим пунктам назначения.
Настроить
Чтобы обеспечить поддержку модулей функций, используйте в файле build.gradle модуля приложения следующие зависимости:
Классный
dependencies { def nav_version = "2.10.2" 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" }
Котлин
dependencies { val nav_version = "2.10.2" 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") }
Обратите внимание, что другие зависимости Navigation должны использовать конфигурации API, чтобы они были доступны для модулей функций.
Основные функции
Чтобы обеспечить поддержку модулей функций, сначала замените все экземпляры 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 сначала проверяет, установлен ли модуль функции. Если модуль функции уже есть, приложение переходит к месту назначения, как и ожидалось. Если модуль отсутствует, приложение показывает промежуточный фрагмент прогресса, пока модуль устанавливается. Реализация фрагмента по умолчанию показывает базовый интерфейс с индикатором выполнения и обрабатывает ошибки установки.
Чтобы настроить этот интерфейс или вручную управлять ходом установки на экране приложения, ознакомьтесь с разделами Настройка фрагмента хода выполнения и Отслеживание состояния запроса в этой статье.
Целевые страницы, для которых не указан параметр app:moduleName, продолжают работать без изменений, как если бы в приложении использовался обычный параметр NavHostFragment.
Настройка фрагмента прогресса
Вы можете переопределить реализацию фрагмента прогресса для каждого графа навигации, установив для атрибута app:progressDestination идентификатор пункта назначения, который вы хотите использовать для обработки хода установки. Ваш собственный пункт назначения должен быть Fragment, производным от AbstractProgressFragment.
Вам необходимо переопределить абстрактные методы для уведомлений о ходе установки, ошибках и других событиях. Затем вы можете показать ход установки в выбранном вами интерфейсе.
Класс DefaultProgressFragment в реализации по умолчанию использует этот API, чтобы показывать ход установки.
Отслеживание статуса запроса
Библиотека Dynamic Navigator позволяет реализовать UX-процесс, аналогичный описанному в разделе Рекомендации по UX для доставки по запросу, в котором пользователь остается в контексте предыдущего экрана, ожидая завершения установки. Это означает, что вам не нужно показывать промежуточный пользовательский интерфейс или фрагмент прогресса.
В этом случае вы несете ответственность за отслеживание и обработку всех состояний установки, изменений хода выполнения, ошибок и т. д.
Чтобы запустить этот неблокирующий процесс навигации, передайте объект 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 Core. Эти обновления содержат события, связанные с ходом установки, которые можно использовать для обновления пользовательского интерфейса. Не забудьте обработать все статусы, как описано в руководстве по Play Core, в том числе запрос подтверждения от пользователя, если это необходимо.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– название пакета модуля.
Используйте правильный graphPackage
Важно правильно указать app:graphPackage, иначе компонент Navigation не сможет включить указанный 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.
Переход к графу навигации include-dynamic
Перейти можно только к startDestination графа навигации include-dynamic. Динамический модуль отвечает за собственный граф навигации, а основное приложение не имеет к нему доступа.
Механизм include-dynamic позволяет основному модулю приложения включать вложенный граф навигации, определенный в динамическом модуле. Этот вложенный граф навигации ведет себя
как любой вложенный граф навигации. Корневой граф навигации (то есть родительский элемент вложенного графа) может определять только сам вложенный граф навигации в качестве пункта назначения, а не его дочерние элементы. Таким образом, startDestination используется, когда целевым является граф include-dynamicnavigation.
Ограничения
- Динамически включаемые графы в настоящее время не поддерживают ссылки на контент.
- Динамически загружаемые вложенные графы (то есть элемент
<navigation>с атрибутомapp:moduleName) в настоящее время не поддерживают ссылки на контент.