Navigare con i moduli delle funzionalità

La libreria Dynamic Navigator estende le funzionalità Componente Navigazione Jetpack per lavorare con le destinazioni definiti in moduli delle funzionalità. Questa libreria fornisce inoltre un'installazione immediata di funzionalità on demand durante la navigazione verso queste destinazioni.

Configura

Per supportare i moduli delle funzionalità, usa le seguenti dipendenze nel file build.gradle del modulo dell'app:

Alla moda

dependencies {
    def nav_version = "2.8.0"

    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.8.0"

    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 per 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"
    ... />

Poi, aggiungi un attributo app:moduleName a qualsiasi elemento <activity>, <fragment> o <navigation> destinazioni nei moduli com.android.dynamic-feature grafici di navigazione associati a DynamicNavHostFragment. Questo attributo indica alla libreria Dynamic Navigator che la destinazione appartiene a un modulo di caratteristiche con il nome da te specificato.

<fragment
    app:moduleName="myDynamicFeature"
    android:id="@+id/featureFragment"
    android:name="com.google.android.samples.feature.FeatureFragment"
    ... />

Quando passi a una di queste destinazioni, la libreria Dynamic Navigator verifica innanzitutto se il modulo delle funzionalità è installato. Se l'elemento è già presente, l'app raggiunge la destinazione come previsto. Se il modulo non è presente, la tua app mostra un frammento di avanzamento intermedio durante l'installazione del modulo. L'implementazione predefinita il frammento di avanzamento mostra un'interfaccia utente di base con una barra di avanzamento e gestisce qualsiasi errori di installazione.

Due schermate di caricamento che mostrano l&#39;UI con una barra di avanzamento durante la navigazione
         a un modulo delle funzionalità per la prima volta
Figura 1. UI che mostra una barra di avanzamento quando un utente naviga a una funzionalità on demand per la prima volta. L'app visualizza questa schermata viene scaricato il modulo corrispondente.

Per personalizzare questa UI o per gestire manualmente l'installazione avanza dalla schermata della tua app, controlla Personalizza il frammento di avanzamento e Monitora le sezioni dello stato della richiesta in questo argomento.

Le destinazioni che non specificano app:moduleName continuano a funzionare senza cambia e si comportano come se la tua app utilizzasse un normale NavHostFragment.

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 da utilizzare per gestire l'avanzamento dell'installazione. I tuoi progressi personalizzati destinazione deve essere una Fragment che deriva da AbstractProgressFragment. Devi eseguire l'override dei metodi astratti per le notifiche sull'installazione avanzamento, errori e altri eventi. Puoi quindi mostrare l'avanzamento dell'installazione in un che preferisci.

L'implementazione predefinita DefaultProgressFragment utilizza questa API per mostrare lo stato di avanzamento dell'installazione.

Monitora lo stato della richiesta

La libreria Dynamic Navigator ti consente di implementare un flusso UX simile uno in Best practice per l'esperienza utente per la distribuzione on demand, in cui un utente rimane nel contesto di una schermata precedente in attesa per completare l'installazione. Ciò significa che non devi mostrare una parte intermedia UI o frammento di avanzamento.

schermata che mostra una barra di navigazione inferiore con un&#39;icona che indica
         che un modulo delle funzionalità è in fase di download
Figura 2. Schermata che mostra l'avanzamento del download da barra di navigazione in basso.

In questo scenario, sei responsabile monitorare e gestire tutti gli stati di installazione, le modifiche di avanzamento, gli errori così via.

Per avviare questo flusso di navigazione non bloccante, passa un DynamicExtras che contiene un oggetto 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 aver chiamato navigate(), devi controllare il valore di installMonitor.isInstallRequired per vedere se il tentativo di navigazione ha generato risultati in un'installazione di un modulo delle funzionalità.

  • Se il valore è false, ti stai dirigendo verso una destinazione normale e non non devi fare altro.
  • Se il valore è true, dovresti iniziare a osservare l'oggetto LiveData che ora si trova in installMonitor.status. Questo oggetto LiveData emette SplitInstallSessionState aggiornamenti dalla libreria Play Core. Questi aggiornamenti contengono l'installazione eventi di avanzamento che puoi usare per aggiornare la UI. Ricorda di gestire tutte le pertinenti come descritto Guida di base di Play, tra cui 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 un Stato SplitInstallSessionStatus.INSTALLED. Devi quindi chiamare NavController.navigate() di nuovo. Poiché il modulo è ora installato, la chiamata ora va a buon fine e l'app raggiunge la destinazione come previsto.

Dopo aver raggiunto lo stato di terminale, ad esempio al termine dell'installazione o quando installazione non riuscita. Devi rimuovere il tuo osservatore LiveData per evitare memoria di fughe di notizie. Puoi verificare se lo stato rappresenta uno stato terminale utilizzando SplitInstallSessionStatus.hasTerminalStatus().

Vedi AbstractProgressFragment per un'implementazione di esempio di questo osservatore.

Grafici inclusi

La libreria Dynamic Navigator supporta l'inclusione di grafici definiti in moduli delle funzionalità. Per includere un grafico definito in un elemento procedi nel seguente modo:

  1. Usa <include-dynamic/> anziché <include/>, come mostrato di seguito esempio:

    <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" />
    
  2. All'interno di <include-dynamic ... />, devi specificare i seguenti attributi:

    • app:graphResName: il nome del file di risorse del grafico di navigazione. La deriva dal nome del file del grafico. Ad esempio, se il grafico è res/navigation/nav_graph.xml, il nome della risorsa è nav_graph.
    • android:id: l'ID destinazione del grafico. La libreria Dynamic Navigator ignora tutti i valori android:id che si trovano nell'elemento principale della incluso nel grafico.
    • app:moduleName: il nome del pacchetto del modulo.

Utilizza il graphicPackage corretto

È importante che i valori app:graphPackage siano corretti per la navigazione non sarà in grado di includere il valore navGraph specificato dalla caratteristica di questo modulo.

Il nome pacchetto di un modulo di funzionalità dinamiche viene creato aggiungendo il tag del modulo al applicationId del modulo dell'app di base. Quindi se Il modulo per l'app di base ha un valore applicationId di com.example.dynamicfeatureapp e il modulo di funzionalità dinamiche è denominato DynamicFeatureModule, quindi il pacchetto il nome del modulo dinamico sarà com.example.dynamicfeatureapp.DynamicFeatureModule. Il nome del pacchetto è sensibile alle maiuscole.

In caso di dubbi, è possibile confermare il nome pacchetto del modulo delle funzionalità controllando il valore AndroidManifest.xml generato. Dopo aver realizzato il progetto, per <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml, che dovrebbe avere un aspetto simile al seguente:

<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 di funzionalità dinamiche e il pacchetto corrisponderà al valore applicationId del modulo dell'app di base. app:graphPackage è la combinazione di questi elementi: com.example.dynamicfeatureapp.DynamicFeatureModule.

È possibile accedere solo al startDestination di un Grafico di navigazione include-dynamic. Il modulo dinamico è responsabile un proprio grafico di navigazione e l'app di base non ne è al corrente.

Il meccanismo include-dinamico consente al modulo dell'app di base di includere un grafico di navigazione nidificato definito nel modulo dinamico. Questo grafico di navigazione nidificato si comporta come qualsiasi grafico di navigazione nidificato. Il grafico di navigazione principale (ovvero l'URL del grafico nidificato) può solo definire il grafico di navigazione nidificato come se e non ai relativi figli. Di conseguenza, startDestination viene utilizzato quando il grafico include- dynamicnavigation è la destinazione.

Limitazioni

  • I grafici inclusi dinamicamente non supportano attualmente i link diretti.
  • Grafici nidificati caricati dinamicamente (ossia un elemento <navigation> con un app:moduleName) al momento non supportano i link diretti.