La libreria Dynamic Navigator estende la funzionalità del componente Navigazione Jetpack per funzionare con le destinazioni definite nei moduli delle funzionalità. Questa libreria consente inoltre l'installazione immediata di moduli di funzionalità on demand durante la navigazione verso queste destinazioni.
Configurazione
Per supportare i moduli delle funzionalità, utilizza le seguenti dipendenze nel file build.gradle
del modulo dell'app:
trendy
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") }
Tieni presente che le altre dipendenze di navigazione devono utilizzare le configurazioni dell'API in modo che siano disponibili per i moduli delle funzionalità.
Utilizzo di base
Per supportare i moduli delle funzionalità, modifica prima tutte le istanze di
NavHostFragment
nella tua app in
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"
... />
Successivamente, aggiungi un attributo app:moduleName
a tutte le destinazioni <activity>
, <fragment>
o <navigation>
nei grafici di navigazione del modulo com.android.dynamic-feature
associati a DynamicNavHostFragment
.
Questo attributo indica alla libreria di Dynamic Navigator che la destinazione
appartiene a un modulo delle funzionalità con il nome da te specificato.
<fragment
app:moduleName="myDynamicFeature"
android:id="@+id/featureFragment"
android:name="com.google.android.samples.feature.FeatureFragment"
... />
Quando navighi verso una di queste destinazioni, la libreria Dynamic Navigator verifica innanzitutto se il modulo delle funzionalità è installato. Se il modulo delle funzionalità è già presente, l'app raggiunge la destinazione come previsto. Se il modulo non è presente, la tua app mostra una destinazione del frammento di avanzamento intermedio durante l'installazione del modulo. L'implementazione predefinita del frammento di avanzamento mostra un'interfaccia utente di base con una barra di avanzamento e gestisce eventuali errori di installazione.
Per personalizzare questa UI o per gestire manualmente l'avanzamento dell'installazione dalla schermata dell'app, consulta le sezioni Personalizzare il frammento di avanzamento e Monitorare lo stato della richiesta in questo argomento.
Le destinazioni che non specificano app:moduleName
continuano a funzionare senza modifiche e si comportano come se la tua app utilizzasse un NavHostFragment
normale.
Personalizza il frammento di avanzamento
Puoi ignorare l'implementazione del frammento di avanzamento per ogni grafico di navigazione impostando l'attributo app:progressDestination
sull'ID della destinazione che vuoi utilizzare per gestire l'avanzamento dell'installazione. La destinazione personalizzata
dell'avanzamento deve essere una
Fragment
che deriva da
AbstractProgressFragment
.
Devi eseguire l'override dei metodi astratti per le notifiche relative a avanzamento dell'installazione, errori e altri eventi. Puoi quindi mostrare l'avanzamento dell'installazione
in un'interfaccia utente a tua scelta.
La classe DefaultProgressFragment
dell'implementazione predefinita utilizza questa API per mostrare l'avanzamento dell'installazione.
Monitorare lo stato della richiesta
La libreria di Dynamic Navigator consente di implementare un flusso UX simile a quello delle best practice UX per la distribuzione on demand, in cui un utente rimane nel contesto di una schermata precedente mentre è in attesa del completamento dell'installazione. Ciò significa che non devi mostrare affatto un'interfaccia utente intermedia o un frammento di avanzamento.
In questo scenario, hai la responsabilità di monitorare e gestire tutti gli stati dell'installazione, le modifiche all'avanzamento, gli errori e così via.
Per avviare questo flusso di navigazione non bloccante, passa un oggetto DynamicExtras
che contiene un DynamicInstallMonitor
a NavController.navigate()
, come mostrato nell'esempio seguente:
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); )
Subito dopo la chiamata a navigate()
, devi controllare il valore di installMonitor.isInstallRequired
per verificare se il tentativo di navigazione ha causato l'installazione di un modulo delle funzionalità.
- Se il valore è
false
, stai navigando verso una destinazione normale e non devi fare altro. Se il valore è
true
, dovresti iniziare a osservare l'oggettoLiveData
che ora si trova ininstallMonitor.status
. Questo oggettoLiveData
emette aggiornamentiSplitInstallSessionState
dalla libreria di base Play. Questi aggiornamenti contengono eventi di avanzamento dell'installazione che puoi utilizzare per aggiornare l'interfaccia utente. Ricorda di gestire tutti gli stati pertinenti come descritto nella guida di Play Core, inclusa la richiesta di conferma dell'utente, se necessario.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); } } }); }
Al termine dell'installazione, l'oggetto LiveData
emette uno
stato SplitInstallSessionStatus.INSTALLED
. Dovresti quindi chiamare di nuovo NavController.navigate()
. Poiché il modulo è stato installato,
la chiamata ha esito positivo e l'app si sposta verso la destinazione come previsto.
Dopo aver raggiunto uno stato di terminale, ad esempio al termine dell'installazione o in caso di errore, devi rimuovere l'osservatore LiveData
per evitare perdite di memoria. Puoi controllare se lo stato rappresenta uno stato del terminale utilizzando SplitInstallSessionStatus.hasTerminalStatus()
.
Vedi AbstractProgressFragment
per un esempio di implementazione di questo osservatore.
Grafici inclusi
La libreria Dynamic Navigator supporta l'inclusione di grafici definiti nei moduli delle funzionalità. Per includere un grafico definito in un modulo delle funzionalità, procedi nel seguente modo:
Utilizza
<include-dynamic/>
invece di<include/>
, come mostrato nell'esempio seguente:<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" />
All'interno di
<include-dynamic ... />
, devi specificare i seguenti attributi:app:graphResName
: il nome del file delle risorse del grafico di navigazione. Il nome deriva dal nome del file del grafico. Ad esempio, se il grafico è inres/navigation/nav_graph.xml
, il nome della risorsa ènav_graph
.android:id
: l'ID di destinazione del grafico. La libreria Dynamic Navigator ignora eventuali valoriandroid:id
presenti nell'elemento principale del grafico incluso.app:moduleName
: il nome del pacchetto del modulo.
Utilizza il GraphPackage corretto
È importante ottenere il valore app:graphPackage
corretto, in quanto, altrimenti, il componente di navigazione non sarà in grado di includere il valore navGraph
specificato dal modulo delle funzionalità.
Il nome del pacchetto di un modulo di funzionalità dinamiche viene creato aggiungendo il
nome del modulo a applicationId
del modulo dell'app di base. Quindi, se il modulo dell'app di base ha un applicationId
di com.example.dynamicfeatureapp
e il modulo delle funzionalità dinamiche è denominato DynamicFeatureModule
, il nome del pacchetto del modulo dinamico sarà com.example.dynamicfeatureapp.DynamicFeatureModule
. Il nome del pacchetto è sensibile alle maiuscole.
In caso di dubbi, puoi confermare il nome del pacchetto del modulo della funzionalità
controllando il valore AndroidManifest.xml
generato. Dopo aver creato il progetto, vai su <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml
, che dovrebbe avere un aspetto simile a questo:
<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>
Il valore featureSplit
deve corrispondere al nome del modulo delle funzionalità dinamiche e il pacchetto corrisponderà al valore applicationId
del modulo dell'app di base. app:graphPackage
è la combinazione dei seguenti elementi: com.example.dynamicfeatureapp.DynamicFeatureModule
.
Accesso a un grafico di navigazione dinamica di inclusione
È possibile accedere solo alla startDestination
di un grafico di navigazione include-dynamic
. Il modulo dinamico è responsabile del proprio
grafico di navigazione e l'app di base non ne è a conoscenza.
Il meccanismo di inclusione-dinamica consente al modulo dell'app di base di includere un
grafico di navigazione nidificato
che viene definito all'interno del modulo dinamico. Questo grafico di navigazione nidificato si comporta
come qualsiasi grafico di navigazione nidificato. Il grafico di navigazione principale (ossia il grafico principale del grafico nidificato) può definire solo il grafico di navigazione nidificato stesso come destinazione e non come i suoi elementi figlio. Pertanto, startDestination
viene utilizzato quando
il grafico di inclusione-navigazione dinamica è la destinazione.
Limitazioni
- Al momento i grafici inclusi dinamicamente non supportano i link diretti.
- I grafici nidificati caricati dinamicamente (ovvero un elemento
<navigation>
conapp:moduleName
) al momento non supportano i link diretti.