Sử dụng mô-đun tính năng để điều hướng

Thư viện Trình điều hướng động sẽ mở rộng chức năng của thành phần Điều hướng Jetpack để liên kết với các đích đến được xác định trong mô-đun tính năng. Thư viện này cũng cung cấp dịch vụ cài đặt liền mạch các mô-đun tính năng theo yêu cầu khi điều hướng đến những đích đến này.

Thiết lập

Để hỗ trợ các mô-đun tính năng, hãy sử dụng các phần phụ thuộc sau trong tệp build.gradle của mô-đun ứng dụng:

Groovy

dependencies {
    def nav_version = "2.5.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"
}

Kotlin

dependencies {
    val nav_version = "2.5.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")
}

Lưu ý các phần phụ thuộc Điều hướng khác phải sử dụng cấu hình api để luôn khả dụng cho các mô-đun tính năng.

Cách sử dụng cơ bản

Để hỗ trợ các mô-đun tính năng, trước tiên hãy thay đổi tất cả các phiên bản của NavHostFragment trong ứng dụng thành 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"
    ... />

Tiếp theo, hãy thêm thuộc tính app:moduleName vào bất kỳ đích đến <activity>, <fragment> hoặc <navigation> nào trong biểu đồ điều hướng của mô-đun com.android.dynamic-feature được liên kết vớiDynamicNavHostFragment. Thuộc tính này báo cho thư viện Trình điều hướng động biết đích đến thuộc một mô-đun tính năng với tên đã được chỉ định.

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

Khi bạn điều hướng đến một trong các đích đến này, trước tiên thư viện Trình điều hướng động sẽ kiểm tra xem mô-đun tính năng có được cài đặt hay không. Nếu đã có mô-đun tính năng, ứng dụng sẽ điều hướng đến đích đến như dự kiến. Nếu không có mô-đun, ứng dụng sẽ hiển thị một đích đến phân mảnh tiến trình trung gian khi cài đặt mô-đun. Cách triển khai mặc định của mảnh tiến trình sẽ hiển thị giao diện người dùng cơ bản ở thanh tiến trình và xử lý mọi lỗi cài đặt.

hai màn hình tải hiển thị giao diện người dùng với thanh tiến trình khi điều hướng đến một mô-đun tính năng lần đầu
Hình 1. Giao diện người dùng hiển thị thanh tiến trình khi người dùng chuyển đến một tính năng theo yêu cầu lần đầu. Ứng dụng sẽ hiển thị màn hình này dưới dạng mô-đun tải xuống tương ứng.

Để tùy chỉnh giao diện người dùng hoặc xử lý theo cách thủ công quá trình cài đặt từ trong màn hình ứng dụng, hãy xemTùy chỉnh phân mảnh tiến trìnhTheo dõi trạng thái yêu cầu trong chủ đề này.

Các đích đến không chỉ định app:moduleName vẫn tiếp tục hoạt động mà không có các thay đổi đồng thời chúng vận hành như thể ứng dụng sử dụng NavHostFragment thông thường.

Tùy chỉnh phân mảnh tiến trình

Có thể ghi đè phương thức triển khai mảnh tiến trình cho từng biểu đồ điều hướng bằng cách đặt thuộc tính app:progressDestination thành mã đích đến mà bạn muốn sử dụng để xử lý tiến trình cài đặt. đích đến tiến trình tùy chỉnh phải là Fragment bắt nguồn từ AbstractProgressFragment. Bạn phải ghi đè các phương thức tóm tắt cho thông báo về tiến trình cài đặt, lỗi và các sự kiện khác. Sau đó, có thể hiển thị tiến trình cài đặt trong giao diện người dùng đã chọn

Lớp triển khai mặc định DefaultProgressFragment sẽ sử dụng API này để hiển thị tiến trình cài đặt.

Theo dõi trạng thái của yêu cầu

Thư viện Trình điều hướng động cho phép triển khai một luồng UX (trải nghiệm người dùng) tương tự như trong các phương pháp hay nhất về UX (trải nghiệm người dùng) để phân phối theo yêu cầu, trong đó người dùng ở bối cảnh màn hình trước khi chờ quá trình cài đặt hoàn tất . Điều này có nghĩa là bạn không cần hiển thị giao diện người dùng trung gian hay mảnh tiến trình.

màn hình hiển thị thanh điều hướng ở dưới cùng với một biểu tượng cho biết mô-đun tính năng đang tải xuống
Hình 2. Màn hình hiển thị tiến trình tải xuống từ thanh điều hướng phía dưới cùng.

Trong trường hợp đó, bạn chịu trách nhiệm theo dõi và xử lý tất cả các trạng thái cài đặt cũng như thay đổi về tiến trình, lỗi, v.v.

Để bắt đầu luồng điều hướng không bị chặn này, hãy chuyển đối tượng DynamicExtras có chứa DynamicInstallMonitor đến NavController.navigate(), như trong ví dụ sau:

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

Ngay sau khi gọi navigate(), bạn phải kiểm tra giá trị của installMonitor.isInstallRequired để xem liệu thao tác điều hướng đã thử có cài đặt mô-đun tính năng hay không.

  • Nếu giá trị là false, tức là bạn đang điều hướng đến một đích đến bình thường và không cần làm gì thêm.
  • Nếu giá trị là true, bạn phải bắt đầu quan sát đối tượng LiveData hiện có trong installMonitor.status. Đối tượng LiveData này phát ra SplitInstallSessionState nội dung cập nhật từ thư viện Play Core. Những bản cập nhật này chứa các sự kiện tiến trình cài đặt mà bạn có thể sử dụng để cập nhật giao diện người dùng. Hãy nhớ xử lý mọi trạng thái liên quan như đã nêu trong hướng dẫn Play Core, bao gồm yêu cầu người dùng xác nhận nếu cần thiết.

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

Khi quá trình cài đặt kết thúc, đối tượng LiveData sẽ phát ra trạng thái SplitInstallSessionStatus.INSTALLED. Sau đó, bạn phải gọi lại hàm NavController.navigate(). Vì hiện mô-đun đã được cài đặt nên cuộc gọi sẽ thành công và ứng dụng sẽ điều hướng đến đích như mong đợi.

Sau khi đạt đến trạng thái đầu cuối, chẳng hạn như khi cài đặt xong hoặc khi cài đặt không thành công, bạn nên xóa bộ quan sát LiveData để tránh bị rò rỉ bộ nhớ. Có thể kiểm tra xem trạng thái của thiết bị đầu cuối có đại diện cho một trạng thái đầu cuối nào hay không bằng cách sử dụng SplitInstallSessionStatus.hasTerminalStatus().

Hãy xem AbstractProgressFragment để biết ví dụ về cách triển khai trình quan sát này.

Biểu đồ đi kèm

Thư viện Trình điều hướng động hỗ trợ cả biểu đồ được xác định trong các mô-đun tính năng. Để sử dụng một biểu đồ được xác định trong mô-đun tính năng, hãy làm như sau:

  1. Sử dụng <include-dynamic/> thay vì <include/>, như hiển thị ở ví dụ sau:

    <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. Bên trong <include-dynamic ... />, bạn phải chỉ định các thuộc tính sau:

    • app:graphResName: tên của tệp tài nguyên biểu đồ điều hướng. Tên lấy từ tên tệp của biểu đồ. Ví dụ: nếu biểu đồ nằm trong res/navigation/nav_graph.xml, thì tên tài nguyên là nav_graph.
    • android:id – mã đích đến của biểu đồ. Thư viện Trình điều hướng động bỏ qua mọi giá trị android:id được tìm thấy trong phần tử gốc của biểu đồ đi kèm.
    • app:moduleName: tên gói của mô-đun.

Sử dụng đúng graphPack

Bạn phải lấy app:graphPackage chính xác, nếu không thành phần Điều hướng sẽ không thể bao gồm navGraph được chỉ định từ mô-đun tính năng.

Tên gói của một mô-đun tính năng linh hoạt được tạo bằng cách thêm tên của mô-đun vào applicationId của mô-đun ứng dụng cơ sở. Vì vậy, nếu mô-đun ứng dụng cơ sở có applicationId trêncom.example.dynamicfeatureapp và mô-đun tính năng linh hoạt được đặt tên là DynamicFeatureModule, tên gói của mô-đun linh hoạt sẽ làcom.example.dynamicfeatureapp.DynamicFeatureModule. Tên gói này có phân biệt chữ hoa và chữ thường.

Nếu không chắc chắn, bạn có thể xác nhận tên gói của mô-đun tính năng bằng cách kiểm tra AndroidManifest.xml được tạo. Sau khi tạo dự án, hãy truy cập vào <DynamicFeatureModule>/build/intermediates/merged_manifest/debug/AndroidManifest.xml, mã này có dạng như sau:

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

Giá trị featureSplit phải khớp với tên của mô-đun tính năng linh hoạt, còn gói sẽ khớp với applicationId của mô-đun ứng dụng cơ sở. app:graphPackage là sự kết hợp của các giá trị này: com.example.dynamicfeatureapp.DynamicFeatureModule.

Bạn chỉ có thể chuyển đến startDestination của biểu đồ điều hướng include-dynamic. Mô-đun linh hoạt chịu trách nhiệm về biểu đồ điều hướng của riêng ứng dụng đó, và ứng dụng cơ sở không nhận biết điều đó.

Cơ chế linh hoạt cho phép mô-đun ứng dụng cơ sở liên kết biểu đồ điều hướng lồng nhau được xác định trong mô-đun linh hoạt. Biểu đồ điều hướng lồng nhau này cũng hoạt động giống các biểu đồ điều hướng lồng nhau khác. Biểu đồ điều hướng gốc (nghĩa là biểu đồ gốc của biểu đồ điều hướng lồng nhau) chỉ có thể xác định chính biểu đồ điều hướng lồng nhau dưới dạng một đích đến chứ không phải là đích đến con của biểu đồ đó. Do đó, startDestination được sử dụng khi biểu đồ điều hướng linh hoạt là đích đến.

Giới hạn

  • Biểu đồ động hiện không hỗ trợ liên kết sâu.
  • Biểu đồ lồng nhau chủ động tải linh hoạt (nghĩa là phần tử <navigation>app:moduleName) hiện không hỗ trợ liên kết sâu.