動態導覽程式庫擴充了 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") }
請注意,其他導覽依附元件應使用 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"
... />
接著,在任一個 <activity>
、<fragment>
中新增 app:moduleName
屬性,或是
位於com.android.dynamic-feature
模組導航圖中與 DynamicNavHostFragment
有關的 <navigation>
目的地。此屬性會告訴動態導覽程式庫,該目的地屬於您指定的功能模組。
<fragment
app:moduleName="myDynamicFeature"
android:id="@+id/featureFragment"
android:name="com.google.android.samples.feature.FeatureFragment"
... />
當您瀏覽至其中一個目的地時,動態導覽程式庫會先檢查是否已安裝該功能模組。如果功能模組已存在,應用程式會如預期地瀏覽至目的地。如果模組不存在,則應用程式在安裝模組時會顯示中繼進度片段目的地。進度片段預設的執行方式,會顯示具有進度列的基本使用者介面,並處理任何發生的安裝錯誤。
若要自訂此使用者介面,或從自有的應用程式畫面手動處理安裝進度,請參閱自訂進度片段及監控要求狀態一節中的這個主題。
未指定 app:moduleName
的目的地會繼續運作且不變更,運作行為也會像應用程式使用一般 NavHostFragment
時一樣。
自訂進度片段
將 app:progressDestination
屬性設為您要用於處理安裝進度的目的地 ID,即可覆寫各個導覽圖在進度片段上的實作內容。自訂進度目的地應為來自於 AbstractProgressFragment
的 Fragment
。您必須覆寫有關安裝進度、錯誤和其他事件通知的摘要方法。接著才可以在您選擇的使用者介面中顯示安裝進度。
DefaultProgressFragment
類別的預設作法會使用到此 API 顯示安裝進度。
監控要求狀態
您可利用動態導覽程式庫實作出與用於隨選提供的使用者體驗最佳做法相似的使用者體驗流程,讓使用者在等待安裝作業完成時可繼續留在上一個畫面中。這表示您完全不需要顯示中繼的使用者介面或進度片段。
在這種情況下,您必須負責監控及處理所有安裝狀態、進度變更和錯誤等。
如要啟動此非封鎖性的導覽流程,請將含有 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
。
包含的圖表
動態導覽程式庫可支援納入在功能模組中定義的圖表。若要納入功能模組中定義的圖表,請採取下列步驟:
請使用
<include-dynamic/>
而非<include/>
,如以下範例所示:<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。動態導覽程式庫會忽略所納入圖表中根元素的任何android:id
值。app:moduleName
:模組的套件名稱。
使用正確的圖形套件
請務必取得正確 app:graphPackage
,否則,導覽元件將無法從功能模組中納入指定的 navGraph
。
動態功能模組套件的名稱,會是將基本應用程式模組的 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
導覽圖中的 startDestination
。動態模組負責其自身的導覽圖,而基本應用程式對其不會有任何認知。
內含的動態機制可讓基本應用程式模組納入在動態模組中定義的巢狀結構導覽圖。此巢狀結構導覽圖的運作方式與任何巢狀結構導覽圖相同。根導覽圖 (也就是父項)
巢狀圖) 只能將巢狀導覽圖本身定義為
目的地而非子項因此,當內含的動態導覽圖是目的地時,就可以使用 startDestination
。
限制
- 內含的動態圖目前不支援深層連結。
- 動態載入的巢狀結構圖(即包含
app:moduleName
的<navigation>
元素)目前不支援深層連結。