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 vẫn tồn tại 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ị gián đoạn 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ó hướng dẫn 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 có cung cấp một phương thức tiện lợi giúp bạn có thể lưu ngăn xếp lui: rememberNavBackStack.

Sử dụng rememberNavBackStack

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

Để 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. Đây đóng vai trò là 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 phụ 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 phụ 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.

Giải pháp thay thế: Lưu trữ trong ViewModel

Một cách tiếp cận 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ị gián đoạn 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 tuần tự hoá 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 đã tuần tự hoá 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 được khôi phục.

Phạm vi của các thực thể ViewModel đối với các đối tượng 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 trong phạm vi 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 bị bật.

Thư viện bổ trợ androidx.lifecycle:lifecycle-viewmodel-navigation3 cung cấp NavEntryDecorator giúp bạn thực hiện việc này. Trình trang trí này cung cấp ViewModelStoreOwner cho mỗi NavEntry. Khi bạn tạo ViewModel bên trong nội dung của NavEntry (ví dụ: sử dụng viewModel() trong Compose), nó sẽ tự động được đặt trong phạm vi 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ị loại bỏ.

Để sử dụng NavEntryDecorator nhằm đặ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 { },
)