Dynamic Navigator ライブラリは、Jetpack Navigation コンポーネントの機能を拡張し、機能モジュールで定義されたデスティネーションと連携して動作するようにします。また、このライブラリを使用して、そうしたデスティネーションに移動したときにオンデマンド機能モジュールのシームレスなインストールを行うことができます。
設定
機能モジュールをサポートするには、アプリ モジュールの build.gradle
ファイルに次の依存関係を指定します。
Groovy
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") }
他の 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
に関連付けられている、com.android.dynamic-feature
モジュールのナビゲーション グラフ内の <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 をカスタマイズする場合、または独自のアプリ画面内でインストール進行状況を手動で処理する場合は、このトピックの進行状況フラグメントをカスタマイズするおよびリクエストの状態をモニタリングするをご覧ください。
app:moduleName
が指定されていないデスティネーションは、アプリが通常の NavHostFragment
を使用している場合と同様に、変更なしで動作します。
進行状況フラグメントをカスタマイズする
各ナビゲーション グラフの進行状況フラグメント実装をオーバーライドするには、インストール進行状況の処理に使用するデスティネーションの ID に app:progressDestination
属性を設定します。カスタムの進行状況デスティネーションは、AbstractProgressFragment
から派生した Fragment
にする必要があります。インストールの進行状況、エラー、その他のイベントについて通知するための抽象メソッドをオーバーライドする必要があります。それにより、独自の UI でインストール進行状況を表示できます。
デフォルト実装の DefaultProgressFragment
クラスは、この API を使用してインストール進行状況を表示します。
リクエストの状態をモニタリングする
Dynamic Navigator ライブラリを使用して、オンデマンド配信の UX に関するおすすめの方法で説明されているのと同様の UX フローを実装できます。このフローでは、ユーザーはインストールが完了するのを待つ間、元の画面のコンテキストにとどまります。つまり、中間 UI や進行状況フラグメントを表示する必要は一切ありません。
このシナリオでは、インストールの状態、進行状況の変化、エラーなどをすべて独自にモニタリングして処理する必要があります。
この非ブロッキング ナビゲーション フローを開始するには、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 ライブラリは、機能モジュールで定義されたグラフのインクルードをサポートします。機能モジュールで定義されたグラフをインクルードするには、次の手順を実施します。
次の例に示すように、
<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" />
<include-dynamic ... />
内で次の属性を指定する必要があります。app:graphResName
: ナビゲーション グラフのリソース ファイルの名前。この名前はグラフのファイル名から導かれます。たとえば、グラフがres/navigation/nav_graph.xml
に含まれている場合、リソース名はnav_graph
です。android:id
: グラフのデスティネーション ID。Dynamic Navigator ライブラリは、インクルードされたグラフのルート要素にあるすべてのandroid:id
値を無視します。app:moduleName
: モジュールのパッケージ名。
正しい graphPackage を使用する
指定された navGraph
を Navigation コンポーネントが機能モジュールからインクルードできるよう、app:graphPackage
を正しく設定することが重要です。
動的機能モジュールのパッケージ名は、ベースアプリ モジュールの applicationId
にモジュール名を追加して作成されます。したがって、ベースアプリ モジュールの com.example.dynamicfeatureapp
が applicationId
であり、動的機能モジュールの名前が DynamicFeatureModule
である場合、動的モジュールのパッケージ名は com.example.dynamicfeatureapp.DynamicFeatureModule
になります。このパッケージ名では大文字と小文字が区別されます。
不確かな場合は、生成された AndroidManifest.xml
をチェックすることで、機能モジュールのパッケージ名を確認できます。プロジェクトをビルドしたら <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml
に移動します。次のようになっているはずです。
<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>
featureSplit
の値は動的機能モジュールの名前と一致し、パッケージはベースアプリ モジュールの applicationId
と一致している必要があります。app:graphPackage
は次のような組み合わせになっています: com.example.dynamicfeatureapp.DynamicFeatureModule
.
include-dynamic ナビゲーション グラフに移動する
include-dynamic
ナビゲーション グラフでは startDestination
にのみ移動できます。動的モジュールでは独自にナビゲーション グラフが作成され、ベースアプリはそれを認識しません。
include-dynamic メカニズムにより、ベースアプリ モジュールは、動的モジュール内で定義されたネストされたナビゲーション グラフをインクルードできます。このネストされたナビゲーション グラフは、他のネストされたナビゲーション グラフと同様に動作します。ルート ナビゲーション グラフ(つまり、
(ネストされたグラフの)で定義できるのは、ネストされたナビゲーション グラフ自体を
子ではなく宛先に属しますしたがって、include-dynamicnavigation グラフがデスティネーションである場合は startDestination
が使用されます。
制限事項
- 現在、動的にインクルードされるグラフはディープリンクをサポートしません。
- 現在、動的に読み込まれるネストグラフ(つまり、
app:moduleName
を含む<navigation>
要素)はディープリンクをサポートしません。