Die Dynamic Navigator Library erweitert die Funktionalität der Jetpack-Navigationskomponente so, dass sie auch mit Zielen arbeitet, die in Funktionsmodulen definiert sind. Diese Bibliothek ermöglicht auch die nahtlose Installation von On-Demand-Funktionsmodulen beim Aufrufen dieser Ziele.
Einrichten
Verwende zur Unterstützung von Funktionsmodulen die folgenden Abhängigkeiten in der Datei build.gradle
deines App-Moduls:
Groovig
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") }
Beachten Sie, dass die anderen Navigationsabhängigkeiten API-Konfigurationen verwenden sollten, damit sie für Ihre Funktionsmodule verfügbar sind.
Grundlegende Verwendung
Zur Unterstützung von Featuremodulen müssen Sie zuerst alle Instanzen von NavHostFragment
in Ihrer App zu androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment
ändern:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.dynamicfeatures.fragment.DynamicNavHostFragment"
app:navGraph="@navigation/nav_graph"
... />
Als Nächstes fügen Sie ein app:moduleName
-Attribut allen <activity>
-, <fragment>
- oder <navigation>
-Zielen in den Navigationsdiagrammen des com.android.dynamic-feature
-Moduls hinzu, die mit einem DynamicNavHostFragment
verknüpft sind.
Dieses Attribut informiert die Dynamic Navigator-Bibliothek darüber, dass das Ziel zu einem Funktionsmodul mit dem von Ihnen angegebenen Namen gehört.
<fragment
app:moduleName="myDynamicFeature"
android:id="@+id/featureFragment"
android:name="com.google.android.samples.feature.FeatureFragment"
... />
Wenn Sie eines dieser Ziele aufrufen, prüft die Dynamic Navigator-Bibliothek zuerst, ob das Funktionsmodul installiert ist. Wenn das Funktionsmodul bereits vorhanden ist, navigiert Ihre App wie erwartet zum Ziel. Wenn das Modul nicht vorhanden ist, zeigt Ihre Anwendung bei der Installation des Moduls ein Zwischenziel für das Fortschrittsfragment an. Die Standardimplementierung des Fortschrittsfragments zeigt eine einfache Benutzeroberfläche mit einer Fortschrittsanzeige und behandelt etwaige Installationsfehler.
In den Abschnitten Fortschrittsfragment anpassen und Anfragestatus überwachen erfahren Sie, wie Sie diese UI anpassen oder den Installationsfortschritt manuell über Ihren eigenen App-Bildschirm verarbeiten.
Ziele ohne app:moduleName
funktionieren weiterhin ohne Änderungen und verhalten sich so, als würde in der Anwendung ein reguläres NavHostFragment
verwendet werden.
Fortschrittsfragment anpassen
Sie können die Implementierung des Fortschrittsfragments für jede Navigationsgrafik überschreiben. Dazu setzen Sie das Attribut app:progressDestination
auf die ID des Ziels, das für die Verarbeitung des Installationsfortschritts verwendet werden soll. Das benutzerdefinierte Fortschrittsziel sollte ein Fragment
sein, der von AbstractProgressFragment
abgeleitet wird.
Für Benachrichtigungen über den Installationsfortschritt, Fehler und andere Ereignisse müssen Sie die abstrakten Methoden überschreiben. Sie können den Installationsfortschritt dann in einer UI Ihrer Wahl anzeigen.
Die Klasse DefaultProgressFragment
der Standardimplementierung verwendet diese API, um den Installationsfortschritt anzuzeigen.
Anfragestatus überwachen
Mit der Dynamic Navigator-Bibliothek können Sie einen UX-Ablauf implementieren, der dem unter UX-Best Practices für On-Demand-Bereitstellung ähnelt, bei dem der Nutzer im Kontext eines vorherigen Bildschirms bleibt, während er auf den Abschluss der Installation wartet. Das bedeutet, dass keine Zwischen-UI und kein Fortschrittsfragment angezeigt werden müssen.
In diesem Szenario sind Sie dafür verantwortlich, alle Installationsstatus, Fortschrittsänderungen, Fehler usw. zu überwachen und zu verarbeiten.
Um diesen nicht blockierenden Navigationsfluss zu starten, übergeben Sie ein DynamicExtras
-Objekt, das einen DynamicInstallMonitor
enthält, an NavController.navigate()
, wie im folgenden Beispiel gezeigt:
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); )
Direkt nach dem Aufrufen von navigate()
sollten Sie den Wert von installMonitor.isInstallRequired
prüfen, um festzustellen, ob der versuchte Navigationsversuch zur Installation eines Feature-Moduls geführt hat.
- Wenn der Wert
false
ist, werden Sie zu einem normalen Ziel navigiert und müssen nichts weiter tun. Wenn der Wert
true
ist, sollten Sie dasLiveData
-Objekt beobachten, das sich jetzt ininstallMonitor.status
befindet. DiesesLiveData
-Objekt sendetSplitInstallSessionState
-Updates aus der Play Core-Bibliothek. Diese Updates enthalten Installationsfortschrittsereignisse, mit denen Sie die Benutzeroberfläche aktualisieren können. Denken Sie daran, alle relevanten Status zu verarbeiten, wie im Play Core-Leitfaden beschrieben. Gegebenenfalls müssen Sie auch die Nutzerbestätigung anfordern.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); } } }); }
Nach Abschluss der Installation gibt das LiveData
-Objekt den Status SplitInstallSessionStatus.INSTALLED
aus. Rufen Sie dann noch einmal NavController.navigate()
auf. Da das Modul jetzt installiert ist, ist der Aufruf jetzt erfolgreich und die Anwendung ruft wie erwartet das Ziel auf.
Wenn Sie einen Terminalstatus erreicht haben, z. B. wenn die Installation abgeschlossen ist oder die Installation fehlschlägt, sollten Sie den LiveData
-Beobachter entfernen, um Speicherlecks zu vermeiden. Mit SplitInstallSessionStatus.hasTerminalStatus()
können Sie prüfen, ob der Status einen Terminalstatus darstellt.
Eine Beispielimplementierung dieses Beobachters finden Sie unter AbstractProgressFragment
.
Eingeschlossene Grafiken
Die Dynamic Navigator-Bibliothek unterstützt das Einbinden von Grafiken, die in Featuremodulen definiert sind. So fügen Sie ein Diagramm ein, das in einem Funktionsmodul definiert ist:
Verwenden Sie
<include-dynamic/>
anstelle von<include/>
, wie im folgenden Beispiel gezeigt:<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" />
Innerhalb von
<include-dynamic ... />
müssen die folgenden Attribute angegeben werden:app:graphResName
: der Name der Ressourcendatei für das Navigationsdiagramm. Der Name wird aus dem Dateinamen der Grafik abgeleitet. Wenn sich das Diagramm beispielsweise inres/navigation/nav_graph.xml
befindet, lautet der Ressourcennamenav_graph
.android:id
: die Ziel-ID der Grafik Die Dynamic Navigator-Bibliothek ignoriert alleandroid:id
-Werte im Stammelement der enthaltenen Grafik.app:moduleName
: der Paketname des Moduls.
Verwende das richtige Diagramm-Paket
Die app:graphPackage
muss korrekt sein, da die Navigationskomponente sonst nicht die angegebene navGraph
aus dem Funktionsmodul einbinden kann.
Der Paketname eines dynamischen Funktionsmoduls wird erstellt, indem der Name des Moduls an die applicationId
des Basis-App-Moduls angehängt wird. Wenn also das applicationId
des Basisanwendungsmoduls com.example.dynamicfeatureapp
und das dynamische Funktionsmodul den Namen DynamicFeatureModule
hat, ist der Paketname des dynamischen Moduls com.example.dynamicfeatureapp.DynamicFeatureModule
. Bei diesem Paketnamen wird die Groß-/Kleinschreibung beachtet.
Im Zweifelsfall kannst du den Paketnamen des Funktionsmoduls bestätigen. Prüfe dazu das generierte AndroidManifest.xml
. Rufen Sie nach dem Erstellen des Projekts <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml
auf. Dies sollte in etwa so aussehen:
<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>
Der Wert für featureSplit
sollte mit dem Namen des dynamischen Funktionsmoduls übereinstimmen und das Paket entspricht dem applicationId
des Basis-App-Moduls. app:graphPackage
ist die Kombination aus diesen Elementen: com.example.dynamicfeatureapp.DynamicFeatureModule
.
Zu einem Diagramm mit dynamischer Navigation navigieren
Es ist nur möglich, zum startDestination
einer include-dynamic
-Navigationsgrafik zu wechseln. Das dynamische Modul ist für sein eigenes Navigationsdiagramm verantwortlich und die Basis-App hat keine Kenntnis davon.
Der Mechanismus zum Einbeziehen dynamischer Elemente ermöglicht, dass das Basis-Anwendungsmodul eine verschachtelte Navigationsgrafik einbinden kann, die im dynamischen Modul definiert ist. Diese verschachtelte Navigationsgrafik
funktioniert wie jede andere verschachtelte Navigationsgrafik. Die Stammnavigationsgrafik, also das übergeordnete Element der verschachtelten Grafik, kann nur die verschachtelte Navigationsgrafik selbst als Ziel und nicht als ihre untergeordneten Elemente definieren. Daher wird startDestination
verwendet, wenn die dynamische Navigationsgrafik das Ziel ist.
Einschränkungen
- In Diagrammen mit dynamisch eingebundenen Inhalten werden derzeit keine Deeplinks unterstützt.
- Dynamisch geladene verschachtelte Grafiken, also ein
<navigation>
-Element mit einemapp:moduleName
-Element, unterstützen derzeit keine Deeplinks.