Google は、黒人コミュニティに対する人種平等の促進に取り組んでいます。取り組みを見る

機能モジュールを使用したナビゲーション

Dynamic Navigator ライブラリは、Jetpack Navigation コンポーネントの機能を拡張し、機能モジュールで定義されたデスティネーションと連携して動作するようにします。また、このライブラリを使用して、そうしたデスティネーションに移動したときにオンデマンド機能モジュールのシームレスなインストールを行うことができます。

設定

機能モジュールをサポートするには、アプリ モジュールの build.gradle ファイルに次の依存関係を指定します。

dependencies {
    def nav_version = "2.3.1"

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

次に、DynamicNavHostFragment に関連付けられたナビゲーション グラフ内の <activity><fragment>、または <navigation> デスティネーションにapp:moduleName 属性を追加します。この属性は、指定された名前を持つ機能モジュールにそのデスティネーションが属していることを Dynamic Navigator ライブラリに伝えます。

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

ユーザーがそうしたデスティネーションのいずれかに移動すると、Dynamic Navigator ライブラリは、まず機能モジュールがインストールされているかどうかを確認します。機能モジュールがすでに存在する場合、アプリは想定されたデスティネーションにナビゲートします。モジュールが存在しない場合、アプリはモジュールをインストールしながら、中間進行状況フラグメントのデスティネーションを表示します。進行状況フラグメントのデフォルト実装は、進行状況バーを含む基本 UI を表示し、すべてのインストール エラーを処理します。

ユーザーが初めて機能モジュールに移動したときに進行状況バーを含む UI を表示する 2 つの読み込み画面
図 1. ユーザーが初めてオンデマンド機能に移動したときに進行状況バーを表示する UI。アプリは、該当モジュールのダウンロード中にこの画面を表示します。

この UI をカスタマイズする場合、または独自のアプリ画面内でインストール進行状況を手動で処理する場合は、このトピックの進行状況フラグメントをカスタマイズするおよびリクエストの状態をモニタリングするをご覧ください。

app:moduleName が指定されていないデスティネーションは、アプリが通常の NavHostFragment を使用している場合と同様に、変更なしで動作します。

進行状況フラグメントをカスタマイズする

各ナビゲーション グラフの進行状況フラグメント実装をオーバーライドするには、インストール進行状況の処理に使用するデスティネーションの ID に app:progressDestination 属性を設定します。カスタムの進行状況デスティネーションは、AbstractProgressFragment から派生した Fragment にする必要があります。インストールの進行状況、エラー、その他のイベントについて通知するための抽象メソッドをオーバーライドする必要があります。それにより、独自の UI でインストール進行状況を表示できます。

デフォルト実装の DefaultProgressFragment クラスは、この API を使用してインストール進行状況を表示します。

リクエストの状態をモニタリングする

Dynamic Navigator ライブラリを使用して、オンデマンド配信の UX に関するおすすめの方法で説明されているのと同様の UX フローを実装できます。このフローでは、ユーザーはインストールが完了するのを待つ間、元の画面のコンテキストにとどまります。つまり、中間 UI や進行状況フラグメントを表示する必要は一切ありません。

機能モジュールがダウンロード中であることを示すアイコンをボトム ナビゲーション バーに表示する画面
図 2. ボトム ナビゲーション バーでダウンロードの進行状況を表示する画面

このシナリオでは、インストールの状態、進行状況の変化、エラーなどをすべて独自にモニタリングして処理する必要があります。

この非ブロッキング ナビゲーション フローを開始するには、DynamicInstallMonitor を含む DynamicExtras オブジェクトを 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 の場合は、現在 installMonitor.status にある LiveData オブジェクトの監視を開始する必要があります。この LiveData オブジェクトは、Play Core ライブラリから SplitInstallSessionState の更新情報を出力します。この更新情報には、UI の更新に使用できるインストール進行状況イベントが含まれています。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 ライブラリは、機能モジュールで定義されたグラフのインクルードをサポートします。機能モジュールで定義されたグラフをインクルードするには、次の手順を実施します。

  1. 次の例に示すように、<include/> の代わりに <include-dynamic/> を使用します。

    <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. <include-dynamic ... /> 内で次の属性を指定する必要があります。

    • app:graphResName: ナビゲーション グラフのリソース ファイルの名前。この名前はグラフのファイル名から導かれます。たとえば、グラフが res/navigation/nav_graph.xml に含まれている場合、リソース名は nav_graph です。
    • android:id: グラフのデスティネーション ID。Dynamic Navigator ライブラリは、インクルードされたグラフのルート要素にあるすべての android:id 値を無視します。
    • app:moduleName: 機能モジュール名。

制限事項

  • 現在、動的にインクルードされるグラフはディープリンクをサポートしません。
  • 現在、動的に読み込まれるネストグラフ(つまり、app:moduleName を含む <navigation> 要素)はディープリンクをサポートしません。

Android Studio のサポート

Navigation Editor で Dynamic Navigator ライブラリを処理するには、バージョン 4.0 以上の Android Studio を使用する必要があります。

それより古いバージョンの Android Studio では、ナビゲーション グラフの XML を直接編集する必要があります。そうしないと、このトピックで説明しているタグまたは属性を含むナビゲーション グラフを開いたときに、例外やその他の未定義の動作が発生する可能性があります。