Lưu và quản lý trạng thái điều hướng

Các phần sau đây mô tả các chiến lược lưu ngăn xếp lui và lưu trữ trạng thái liên kết với các mục trong ngăn xếp lui.

Lưu ngăn xếp lui

Việc đảm bảo trạng thái điều hướng của ứng dụng duy trì trên nhiều sự kiện trong vòng đời, bao gồm cả các thay đổi về cấu hình và quá trình bị buộc tắt là rất quan trọng để mang lại trải nghiệm tốt cho người dùng. Trong Navigation 3, bạn sở hữu ngăn xếp lui của mình, vì vậy, không có nguyên tắc nghiêm ngặt về cách bạn nên tạo hoặc lưu ngăn xếp đó. Tuy nhiên, Navigation 3 cung cấp một phương thức tiện lợi giúp bạn có một ngăn xếp lui có thể lưu: rememberNavBackStack.

Sử dụng rememberNavBackStack

Hàm composable rememberNavBackStack được thiết kế để tạo một ngăn xếp lui duy trì trên các thay đổi về cấu hình và bị buộc tắt.

Để rememberNavBackStack hoạt động chính xác, mỗi khoá trong ngăn xếp lui phải tuân thủ các yêu cầu cụ thể:

  • Triển khai giao diện NavKey: Mọi khoá trong ngăn xếp lui phải triển khai giao diện NavKey. Giao diện này hoạt động như một giao diện đánh dấu báo hiệu cho thư viện rằng khoá có thể được lưu.
  • Có chú thích @Serializable: Ngoài việc triển khai NavKey, các lớp và đối tượng khoá của bạn phải được đánh dấu bằng chú thích @Serializable.

Đoạn mã sau đây cho thấy cách triển khai chính xác rememberNavBackStack:

@Serializable
data object Home : NavKey

@Composable
fun NavBackStack() {
    val backStack = rememberNavBackStack(Home)
}

Ghi nhớ ngăn xếp lui bằng các kiểu con của NavKey

Hàm composable rememberNavBackStack trả về NavBackStack<NavKey>. Nếu ứng dụng của bạn xác định kiểu con riêng của NavKey mà tất cả các khoá của ứng dụng đều kế thừa, thì bạn có thể giữ nguyên kiểu đó bằng cách triển khai một hàm ghi nhớ tuỳ chỉnh như sau:

@Serializable
sealed interface MyAppNavKey : NavKey

@Serializable
data object ScreenA: MyAppNavKey

@Serializable
data class ScreenB(val id: String): MyAppNavKey

@Composable
fun rememberMyAppNavBackStack(vararg elements: MyAppNavKey): NavBackStack<MyAppNavKey> {
    return rememberSerializable(serializer = serializer()) {
        NavBackStack(*elements)
    }
}

@Composable
fun MyApp() {
    // defaultNavBackStack is NavBackStack<NavKey>
    val defaultNavBackStack = rememberNavBackStack(ScreenA)
    // myAppNavBackStack is NavBackStack<MyAppNavKey>
    val myAppNavBackStack = rememberMyAppNavBackStack(ScreenA)
}

Để xem thêm các ví dụ (bao gồm cả cách xử lý tính đa hình mở), hãy xem NavBackStackSamples.

Cách thay thế: Lưu trữ trong ViewModel

Một phương pháp khác để quản lý ngăn xếp lui là lưu trữ ngăn xếp đó trong ViewModel. Để duy trì trạng thái trong quá trình bị buộc tắt khi sử dụng ViewModel hoặc bất kỳ bộ nhớ tuỳ chỉnh nào khác, bạn cần:

  • Đảm bảo các khoá của bạn có thể được tuần tự hoá: Giống như với rememberNavBackStack, các khoá điều hướng của bạn phải có thể được tuần tự hoá.
  • Xử lý quá trình chuyển đổi tuần tự và giải tuần tự hoá theo cách thủ công: Bạn chịu trách nhiệm lưu thủ công bản trình bày đã chuyển đổi tuần tự của từng khoá vào bộ nhớ liên tục và giải tuần tự hoá khoá đó từ bộ nhớ liên tục (ví dụ: SharedPreferences, cơ sở dữ liệu hoặc tệp) khi ứng dụng của bạn chuyển sang chạy ở chế độ nền hoặc đang được khôi phục.

Phạm vi ViewModel cho NavEntry

ViewModels được dùng để giữ lại trạng thái liên quan đến giao diện người dùng trên các thay đổi về cấu hình, chẳng hạn như thao tác xoay màn hình. Theo mặc định, ViewModels được đặt phạm vi cho ViewModelStoreOwner gần nhất, thường là Activity hoặc Fragment.

Tuy nhiên, bạn có thể muốn đặt phạm vi ViewModel cho một NavEntry cụ thể (tức là một màn hình hoặc đích đến cụ thể) trên ngăn xếp lui, thay vì toàn bộ Activity. Điều này đảm bảo rằng trạng thái của ViewModel chỉ được giữ lại khi NavEntry cụ thể đó là một phần của ngăn xếp lui và sẽ bị xoá khi NavEntry được bật.

Thư viện bổ trợ androidx.lifecycle:lifecycle-viewmodel-navigation3 cung cấp một NavEntryDecorator giúp thực hiện việc này. Trình trang trí này cung cấp một ViewModelStoreOwner cho mỗi NavEntry. Khi bạn tạo một ViewModel bên trong nội dung của NavEntry (ví dụ: sử dụng viewModel() trong Compose), thì ViewModel đó sẽ tự động được đặt phạm vi cho khoá của NavEntry cụ thể đó trên ngăn xếp lui. Điều này có nghĩa là ViewModel được tạo khi NavEntry được thêm vào ngăn xếp lui và bị xoá khi NavEntry bị xoá.

Để sử dụng NavEntryDecorator cho việc đặt phạm vi ViewModel cho NavEntry, hãy làm theo các bước sau:

  1. Thêm phần phụ thuộc androidx.lifecycle:lifecycle-viewmodel-navigation3 vào tệp app/build.gradle.kts.
  2. Thêm rememberSaveableStateHolderNavEntryDecorator() mặc định vào danh sách entryDecorators khi tạo NavDisplay.
  3. Thêm rememberViewModelStoreNavEntryDecorator() vào danh sách entryDecorators.

NavDisplay(
    entryDecorators = listOf(
        // Add the default decorators for managing scenes and saving state
        rememberSaveableStateHolderNavEntryDecorator(),
        // Then add the view model store decorator
        rememberViewModelStoreNavEntryDecorator()
    ),
    backStack = backStack,
    entryProvider = entryProvider { },
)