Nawigacja z użyciem modułów funkcji

Biblioteka Dynamic Navigator rozszerza funkcjonalność Komponent Nawigacji Jetpack do obsługi miejsc docelowych. zdefiniowane w moduły funkcji. Ta biblioteka umożliwia też bezproblemową instalację funkcji na żądanie. modułów podczas przechodzenia do tych miejsc docelowych.

Konfiguracja

Aby obsługiwać moduły funkcji, użyj tych zależności w pliku build.gradle modułu aplikacji:

Groovy

dependencies {
    def nav_version = "2.8.4"

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

    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")
}

Pamiętaj, że inne zależności nawigacji powinny używać konfiguracji API. aby były one dostępne dla modułów funkcji.

Podstawowe wykorzystanie

Aby obsługiwać moduły funkcji, najpierw zmień wszystkie wystąpienia NavHostFragment w aplikacji, aby 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"
    ... />

Następnie dodaj atrybut app:moduleName do dowolnych <activity>, <fragment> lub <navigation> miejsca docelowe w module com.android.dynamic-feature wykresy nawigacyjne powiązane z DynamicNavHostFragment. Ten atrybut informuje bibliotekę Dynamic Navigator, że miejsce docelowe należy do modułu funkcji o podanej przez Ciebie nazwie.

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

Po przejściu do jednego z tych miejsc biblioteka Dynamic Navigator najpierw sprawdza, czy jest zainstalowany moduł funkcji. Jeśli funkcja już jest dostępny, aplikacja przejdzie do miejsca docelowego zgodnie z oczekiwaniami. Jeśli moduł nie jest dostępny, aplikacja wyświetla pośredni fragment postępu podczas instalowania modułu. Domyślna implementacja Fragment postępu pokazuje podstawowy interfejs użytkownika z paskiem postępu i obsługuje błędów instalacji.

2 ekrany wczytywania, które pokazują interfejs z paskiem postępu podczas nawigacji.
         do modułu funkcji.
Rysunek 1. Interfejs przedstawiający pasek postępu poruszający się przez użytkownika. do funkcji na żądanie. Aplikacja wyświetla ten ekran jako odpowiedni moduł.

Aby dostosować ten interfejs użytkownika lub ręcznie obsługiwać instalację postępów na ekranie swojej aplikacji, zobacz Dostosuj fragment postępu i Sekcje Monitorowanie stanu żądania w tym temacie.

Miejsca docelowe bez wartości app:moduleName będą nadal działać bez zmienia się i zachowuje tak, jakby aplikacja używała zwykłego NavHostFragment.

Dostosuj fragment postępu

Możesz zastąpić implementację fragmentu postępu w przypadku każdego wykresu nawigacyjnego przez ustawienie atrybutu app:progressDestination na identyfikator miejsca docelowego który ma być używany do obsługi postępu instalacji. Twój postęp niestandardowy miejsce docelowe powinno być Fragment, które pochodzi z AbstractProgressFragment Musisz zastąpić abstrakcyjne metody w przypadku powiadomień o instalacji postęp, błędy i inne zdarzenia. Następnie możesz wyświetlić postęp instalacji w interfejsu użytkownika.

Implementacja domyślna DefaultProgressFragment klasa używa tego interfejsu API do pokazywania postępu instalacji.

Monitorowanie stanu żądania

Biblioteka Dynamic Navigator umożliwia wdrożenie procesu UX podobnego do jedno w Sprawdzone metody UX w zakresie dostarczania na żądanie, gdy użytkownik pozostaje w kontekście poprzedniego ekranu, czekając na aby zakończyć instalację. Oznacza to, że nie musisz wyświetlać poziomu średniozaawansowanego interfejsu użytkownika ani fragmentu postępu.

ekran z dolnym paskiem nawigacyjnym z ikoną
         który jest pobierany
Rysunek 2. Ekran pokazujący postęp pobierania z dolny pasek nawigacyjny.

W tym scenariuszu odpowiadasz za monitorowania i obsługi wszystkich stanów instalacji, zmian postępu, błędów oraz i tak dalej.

Aby rozpocząć ten nieblokujący proces nawigacji, przekaż DynamicExtras obiekt zawierający DynamicInstallMonitor do NavController.navigate(), jak w tym przykładzie:

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);
)

Bezpośrednio po wywołaniu funkcji navigate() sprawdź wartość installMonitor.isInstallRequired, aby sprawdzić, czy próba nawigacji spowodowała w ramach instalacji modułów funkcji.

  • Jeśli wartość to false, przechodzisz do normalnego miejsca docelowego i nie nie muszą robić nic więcej.
  • Jeśli wartość to true, należy zacząć obserwować obiekt LiveData, który jest teraz w domenie installMonitor.status. Ten obiekt LiveData emituje SplitInstallSessionState. i aktualizacje z biblioteki Play Core. Te aktualizacje obejmują instalację zdarzeń postępu, których możesz używać do aktualizowania interfejsu użytkownika. Pamiętaj o załatwianiu wszystkich spraw odpowiednie stany opisane w Przewodnik po Google Play Core w tym prośba o potwierdzenie użytkownika w razie potrzeby.

    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);
              }
          }
      });
    }
    

Po zakończeniu instalacji obiekt LiveData wysyła komunikat Stan SplitInstallSessionStatus.INSTALLED. Następnie należy zadzwonić NavController.navigate(). Moduł jest już zainstalowany, więc wywołanie teraz się powiedzie, a aplikacja przejdzie do miejsca docelowego zgodnie z oczekiwaniami.

Po osiągnięciu stanu terminala, np. po zakończeniu instalacji lub po instalacja się nie powiedzie, usuń obserwatora LiveData, aby uniknąć pamięci wyciek danych. Aby sprawdzić, czy stan wskazuje stan terminala, użyj funkcji SplitInstallSessionStatus.hasTerminalStatus()

Zobacz AbstractProgressFragment aby zobaczyć przykład zastosowania funkcji tego obserwatora.

Uwzględnione wykresy

Biblioteka Dynamic Navigator obsługuje wstawianie wykresów zdefiniowanych w modułów funkcji. Aby dołączyć wykres zdefiniowany w funkcji wykonaj te czynności:

  1. Użyj <include-dynamic/> zamiast <include/>, jak pokazano poniżej przykład:

    <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. W usłudze <include-dynamic ... /> musisz określić te atrybuty:

    • app:graphResName: nazwa pliku zasobów grafu nawigacyjnego. pochodzi z nazwy pliku wykresu. Jeśli na przykład wykres jest w res/navigation/nav_graph.xml, nazwa zasobu to nav_graph.
    • android:id – identyfikator miejsca docelowego wykresu. Biblioteka Dynamic Navigator ignoruje wszystkie wartości android:id, które znajdują się w elemencie głównym argumentu uwzględniony wykres.
    • app:moduleName: nazwa pakietu modułu.

Użyj właściwego pakietu wykresów

Ważne jest, aby app:graphPackage była poprawna jako nawigacja komponent nie może dołączyć określonego elementu navGraph z funkcji W przeciwnym razie.

Nazwa pakietu modułu funkcji dynamicznych jest tworzona przez dołączenie parametru nazwa modułu na applicationId podstawowego modułu aplikacji. Jeśli więc podstawowy moduł aplikacji ma applicationId o wartości com.example.dynamicfeatureapp oraz moduł funkcji dynamicznych nazywa się DynamicFeatureModule, a pakiet nazwa modułu dynamicznego będzie com.example.dynamicfeatureapp.DynamicFeatureModule Nazwa pakietu to .

W razie wątpliwości możesz potwierdzić nazwę pakietu modułu funkcji. sprawdzając wygenerowane dane AndroidManifest.xml. Po utworzeniu projektu przejdź do: do: <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml, Powinien on wyglądać mniej więcej tak:

<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>

Wartość featureSplit powinna być zgodna z nazwą modułu funkcji dynamicznych, a pakiet pasuje do applicationId podstawowego modułu aplikacji. app:graphPackage to kombinacja tych elementów: com.example.dynamicfeatureapp.DynamicFeatureModule.

Można przejść tylko do startDestination Wykres nawigacyjny include-dynamic. Moduł dynamiczny odpowiada za a aplikacja podstawowa o tym nie wie.

Mechanizm include-dynamiczny pozwala w podstawowym module aplikacji zawierać element zagnieżdżony wykres nawigacyjny zdefiniowanej w module dynamicznym. Ten zagnieżdżony wykres nawigacyjny zachowuje się jak każdy zagnieżdżony wykres nawigacyjny. Główny wykres nawigacyjny (czyli nadrzędny zagnieżdżonego wykresu) może zostać zdefiniowany jedynie jako zagnieżdżony wykres nawigacyjny miejsca docelowego, a nie jego elementów podrzędnych. Dlatego startDestination jest używany, gdy a wykres uwzględnia-dynamiczną nawigację jest miejscem docelowym.

Ograniczenia

  • Wykresy dołączane dynamicznie nie obsługują obecnie precyzyjnych linków.
  • Dynamicznie ładowane wykresy zagnieżdżone (element <navigation> z parametrem app:moduleName) nie obsługują obecnie precyzyjnych linków.