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 quay lại và lưu trữ trạng thái liên kết với các mục trong ngăn xếp quay lại.

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ì trong nhiều sự kiện 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à điều 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 quay lại, 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 này. Tuy nhiên, Navigation 3 cung cấp một phương thức thuận tiện giúp bạn có được ngăn xếp lui có thể lưu: rememberNavBackStack.

Sử dụng rememberNavBackStack

Hàm có khả năng kết hợp rememberNavBackStack được thiết kế để tạo một ngăn xếp lui vẫn tồn tại khi cấu hình thay đổi 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 quay lại 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 quay lại đều phải triển khai giao diện NavKey. Đây là 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, bạn phải đánh dấu các lớp và đối tượng khoá bằng chú thích @Serializable.

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

@Serializable
data object Home : NavKey

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

Phương án thay thế: Lưu trữ trong ViewModel

Một cách khác để quản lý ngăn xếp lui là lưu trữ ngăn xếp đó trong một ViewModel. Để duy trì dữ liệu khi ứng dụng bị buộc tắt khi 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 chuyển đổi tuần tự: Giống như với rememberNavBackStack, các khoá điều hướng của bạn phải có thể được chuyển đổi tuần tự.
  • Xử lý quy trình chuyển đổi tuần tự và chuyển đổi ngược tuần tự theo cách thủ công: Bạn có trách nhiệm lưu biểu thị đã chuyển đổi tuần tự của từng khoá vào bộ nhớ liên tục theo cách thủ công và chuyển đổi ngược tuần tự 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 nền hoặc đang được khôi phục.

Phạm vi từ ViewModel giây đến NavEntry giây

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

Tuy nhiên, bạn có thể muốn đặt phạm vi cho một ViewModel thành 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 lùi, 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 trong 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 lên.

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. Decorator 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), 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ị loại bỏ.

Để sử dụng NavEntryDecorator cho việc đặt phạm vi ViewModel thành 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() vào danh sách entryDecorators khi tạo NavDisplay.
  3. Thêm các đối tượng trang trí khác vào NavDisplay.

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