Lưu trạng thái với các mảnh

Một số hoạt động của hệ thống Android có thể ảnh hưởng đến trạng của thái mảnh. Để đảm bảo trạng thái người dùng được lưu lại, khung Android sẽ tự động lưu và khôi phục các mảnh cùng với ngăn xếp lui. Do đó, bạn cần đảm bảo đã lưu và khôi phục mọi dữ liệu trong mảnh.

Bảng dưới đây tóm tắt các hoạt động khiến mảnh mất trạng thái và liệu các loại trạng thái này có bị ảnh hưởng bởi các thay đổi đó hay không. Các loại trạng thái được đề cập trong bảng bao gồm:

  • Biến (Variables): biến cục bộ trong mảnh.
  • Trạng thái thành phần hiển thị (view state): bất kỳ dữ liệu nào phụ thuộc vào một hoặc nhiều chế độ xem trong mảnh.
  • SavedState: dữ liệu vốn có của bản sao mảnh này sẽ được lưu trong onSaveInstanceState().
  • NonConfig: dữ liệu được lấy từ một nguồn bên ngoài, chẳng hạn như một máy chủ hoặc kho lưu trữ cục bộ, hoặc dữ liệu tạo ra bởi người dùng được gửi đến máy chủ sau quá trình chuyển phát.

Thông thường, Biến sẽ được xử lý giống như SavedState, dù vậy bảng dưới đây sẽ giúp bạn phân biệt các tác động khác nhau của từng hoạt động lên mỗi trạng thái.

Hoạt động Biến Trạng thái thành phần hiển thị SavedState NonConfig
Đã thêm vào ngăn xếp lui x
Thay đổi cấu hình x
Quá trình huỷ/tái tạo x ✓*
Đã xóa và không được thêm vào ngăn xếp lui x x x x
Đã lưu trữ x x x x

* Bạn có thể giữ nguyên trạng thái NonConfig trong suốt quá trình bị buộc tắt bằng cách sử dụng Mô-đun Trạng thái đã lưu của ViewModel.

Bảng 1: Các hoạt động huỷ mảnh khác nhau và ảnh hưởng của những hoạt động đó đến từng loại trạng thái.

Hãy xem một ví dụ cụ thể. Xét màn hình tạo một chuỗi ngẫu nhiên, hiển thị chuỗi đó trong TextView và cung cấp tùy chọn chỉnh sửa chuỗi đó trước khi gửi cho bạn bè:

ứng dụng tạo văn bản ngẫu nhiên biểu diễn nhiều loại trạng thái
Hình 1. Ứng dụng tạo văn bản ngẫu nhiên thể hiện nhiều loại trạng thái khác nhau.

Với ví dụ này, giả sử sau khi người dùng nhấn nút chỉnh sửa, ứng dụng sẽ hiển thị chế độ xem EditText, tại đó người dùng có thể chỉnh sửa thông báo. Nếu người dùng nhấp vào CANCEL, chế độ xem EditText sẽ bị xóa và chế độ hiển thị sẽ được đặt thành View.GONE. Một màn hình như vậy yêu cầu quản lý 4 mẩu dữ liệu để đảm bảo trải nghiệm liền mạch:

Dữ liệu Loại Loại trạng thái Nội dung mô tả
seed Long NonConfig Dữ liệu gốc được dùng để tạo ngẫu nhiên một hoạt động mới phù hợp. Được tạo cùng lúc với ViewModel.
randomGoodDeed String SavedState + Variable Sinh ra khi mảnh được tạo lần đầu tiên. randomGoodDeed được lưu để đảm bảo người dùng thấy được hoạt động trước đó kể cả sau khi quá trình bị buộc tắt và tái tạo diễn ra.
isEditing Boolean SavedState + Variable Cờ boolean có giá trị true khi người dùng bắt đầu chỉnh sửa. isEditing được lưu để đảm bảo rằng phần chỉnh sửa của màn hình vẫn hiện lên trong quá trình mảnh được tái tạo.
Văn bản được chỉnh sửa Editable View State (thuộc sở hữu của EditText) Văn bản được chỉnh sửa trong chế độ xem EditText. Chế độ xem EditText sẽ lưu văn bản này để đảm bảo những thay đổi đang diễn ra của người dùng sẽ không mất đi.

Bảng 2: Các trạng thái mà ứng dụng tạo văn bản ngẫu nhiên quản lý.

Các phần sau đây mô tả cách quản lý trạng thái của dữ liệu đúng cách thông qua các hoạt động huỷ.

Trạng thái thành phần hiển thị

Chế độ xem chịu trách nhiệm quản lý chính trạng thái của chế độ xem này. Ví dụ: khi một chế độ xem chấp nhận hoạt động đầu vào của người dùng, bạn có trách nhiệm lưu và khôi phục hoạt động đó để xử lý các thay đổi về cấu hình. Tất cả các chế độ xem do khung Android cung cấp đều có cách triển khai onSaveInstanceState()onRestoreInstanceState() riêng, vì vậy, bạn không cần quản lý trạng thái thành phần hiển thị trong mảnh.

Ví dụ: trong trường hợp trước, chuỗi đã chỉnh sửa được lưu trong EditText. EditText xác định giá trị của văn bản đang hiển thị, cũng như các thông tin chi tiết khác, chẳng hạn như phần đầu và phần cuối của bất kỳ văn bản đã chọn nào.

Một chế độ xem cần có mã nhận dạng để giữ nguyên trạng thái hoạt động. Mã này phải là duy nhất trong mảnh và hệ phân cấp chế độ xem. Các chế độ xem không có mã nhận dạng sẽ không thể giữ nguyên trạng thái hoạt động.

<EditText
    android:id="@+id/good_deed_edit_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

Như đã đề cập trong bảng 1, các chế độ xem sẽ lưu và khôi phục ViewState thông qua tất cả các hoạt động không loại bỏ mảnh hoặc hủy nơi lưu trữ.

SavedState

Mảnh chịu trách nhiệm quản lý một số lượng nhỏ trạng thái động không ảnh hưởng đến hoạt động của mảnh. Bạn có thể giữ nguyên dữ liệu chuyển đổi tuần tự dễ dàng bằng cách sử dụng Fragment.onSaveInstanceState(Bundle). Tương tự như Activity.onSaveInstanceState(Bundle), dữ liệu đặt trong gói sẽ được giữ nguyên sau khi thay đổi cấu hình cùng với quá trình bị buộc tắt và tái tạo, đồng thời có sẵn trong các phương thức onCreate(Bundle), onCreateView(LayoutInflater, ViewGroup, Bundle)onViewCreated(View, Bundle) của mảnh.

Tiếp tục với ví dụ trước, randomGoodDeed là hành động được hiển thị cho người dùng và isEditing là một cờ để xác định liệu mảnh sẽ hiện hay ẩn EditText. Trạng thái đã lưu này phải được duy trì bằng cách sử dụng onSaveInstanceState(Bundle), như minh họa trong ví dụ sau:

Kotlin

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    outState.putBoolean(IS_EDITING_KEY, isEditing)
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}

Java

@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(IS_EDITING_KEY, isEditing);
    outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed);
}

Để khôi phục trạng thái trong onCreate(Bundle), hãy truy xuất giá trị đã lưu trữ của gói đó:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
    randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
            ?: viewModel.generateRandomGoodDeed()
}

Java

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (savedInstanceState != null) {
        isEditing = savedInstanceState.getBoolean(IS_EDITING_KEY, false);
        randomGoodDeed = savedInstanceState.getString(RANDOM_GOOD_DEED_KEY);
    } else {
        randomGoodDeed = viewModel.generateRandomGoodDeed();
    }
}

Như đã đề cập trong bảng 1, hãy lưu ý rằng các biến sẽ được giữ nguyên khi mảnh được đặt trong ngăn xếp lui. Việc xem các biến này như trạng thái đã lưu giúp chúng giữ nguyên kể cả khi xuất hiện hoạt động huỷ.

NonConfig

Bạn không nên đặt NonConfig bên ngoài mảnh của mình, chẳng hạn như trong ViewModel. Trong ví dụ trên, seed (trạng thái NonConfig) được tạo trong ViewModel. Nguyên lý duy trì trạng thái của thuộc tính này là do ViewModel sở hữu.

Kotlin

public class RandomGoodDeedViewModel : ViewModel() {
    private val seed = ... // Generate the seed

    private fun generateRandomGoodDeed(): String {
        val goodDeed = ... // Generate a random good deed using the seed
        return goodDeed
    }
}

Java

public class RandomGoodDeedViewModel extends ViewModel {
    private Long seed = ... // Generate the seed

    private String generateRandomGoodDeed() {
        String goodDeed = ... // Generate a random good deed using the seed
        return goodDeed;
    }
}

Lớp ViewModel thường cho phép dữ liệu tồn tại sau khi thay đổi về cấu hình, chẳng hạn như xoay màn hình, và vẫn nằm trong bộ nhớ khi mảnh được đặt ở ngăn xếp lui. Sau quá trình bị buộc tắt và tái tạo, ViewModel sẽ được tạo lại, đi kèm với một seed mới. Việc thêm mô-đun SavedState vào ViewModel cho phép ViewModel giữ nguyên trạng thái đơn giản sau quá trình bị buộc tắt và tái tạo.

Tài nguyên khác

Để biết thêm thông tin về cách quản lý trạng thái mảnh, hãy xem các tài nguyên bổ sung sau.

Lớp học lập trình

Hướng dẫn