Thao tác lưu trạng thái giao diện người dùng

Việc kịp thời duy trì và khôi phục trạng thái giao diện người dùng của một hoạt động sau khi hoàn tất quá trình huỷ bỏ hoạt động hoặc ứng dụng mà hệ thống bắt đầu là một phần quan trọng trong trải nghiệm người dùng. Trong những trường hợp này, người dùng mong muốn trạng thái giao diện người dùng vẫn giữ nguyên, trong khi đó, hệ thống lại huỷ bỏ hoạt động và bất kỳ trạng thái nào được lưu trữ trong đó.

Để làm cầu nối giữa kỳ vọng của người dùng và hành vi hệ thống, sử dụng kết hợp đối tượng ViewModel, trạng thái bản sao đã lưu bao gồm API onSaveInstanceState() trong hệ thống View, rememberSaveable trong Jetpack Compose và SavedStateHandle trong ViewModel và/hoặc bộ nhớ cục bộ để duy trì trạng thái giao diện người dùng trong ứng dụng đó và chuyển đổi thực thể Activity. Việc quyết định cách kết hợp các tuỳ chọn này phụ thuộc vào độ phức tạp của dữ liệu giao diện người dùng, trường hợp sử dụng của ứng dụng và việc cân nhắc giữa tốc độ có sẵn của dữ liệu so với mức sử dụng bộ nhớ.

Dù lựa chọn phương pháp tiếp cận nào, bạn cũng phải đảm bảo ứng dụng của mình đáp ứng sự kỳ vọng của người dùng về trạng thái giao diện người dùng và cung cấp một giao diện người dùng mượt mà, gọn gàng (tránh thời gian trễ khi tải dữ liệu vào giao diện người dùng, đặc biệt sau các lần thay đổi cấu hình thường xuyên, chẳng hạn như thao tác xoay). Trong hầu hết các trường hợp, bạn nên sử dụng kết hợp nhiều phương pháp khác nhau để có được trải nghiệm người dùng tốt nhất trong ứng dụng.

Trang này thảo luận về kỳ vọng của người dùng về trạng thái giao diện người dùng, các tuỳ chọn có sẵn để duy trì trạng thái, những sự đánh đổi và hạn chế của mỗi trạng thái.

Kỳ vọng người dùng và hành vi hệ thống

Tuỳ thuộc vào hành động mà người dùng thực hiện, người dùng mong muốn hệ thống sẽ xoá hoặc duy trì trạng thái hoạt động. Trong một số trường hợp, hệ thống tự động làm những việc người dùng mong đợi. Trong các trường hợp khác, hệ thống lại làm ngược lại những gì người dùng mong đợi.

Thao tác đóng trạng thái giao diện người dùng do người dùng bắt đầu

Người dùng muốn khi họ bắt đầu một hoạt động, trạng thái tạm thời của giao diện người dùng của hoạt động đó sẽ vẫn giữ nguyên cho đến khi người dùng đóng hoàn toàn hoạt động đó. Người dùng có thể đóng hoàn toàn một hoạt động bằng các cách sau:

  • Vuốt hoạt động ra khỏi màn hình Tổng quan (Gần đây)
  • Tắt hoặc buộc thoát khỏi ứng dụng từ màn hình Cài đặt.
  • Khởi động lại thiết bị.
  • Hoàn tất một số thao tác "hoàn tất" (được Activity.finish() hỗ trợ).

Giả định của người dùng trong những trường hợp đóng hoàn toàn hoạt động này là họ đã điều hướng vĩnh viễn khỏi hoạt động và nếu mở lại hoạt động, họ muốn hoạt động sẽ bắt đầu từ một trạng thái mới. Hành vi cơ bản của hệ thống trong các tình huống đóng hoạt động này khớp với kỳ vọng của người dùng – thực thể của hoạt động sẽ bị huỷ và xoá khỏi bộ nhớ, đồng thời, mọi trạng thái được lưu trữ trong đó và mọi bản ghi trạng thái của thực thể đã lưu có liên kết với hoạt động đó cũng sẽ bị xoá bỏ.

Có một số ngoại lệ đối với quy tắc về thao tác đóng hoàn toàn này – ví dụ: Người dùng có thể muốn trình duyệt đưa họ đến đúng trang web mà họ đang xem trước khi thoát khỏi trình duyệt bằng cách sử dụng nút quay lại.

Thao tác đóng trạng thái giao diện người dùng do hệ thống bắt đầu

Người dùng muốn trạng thái giao diện người dùng của một hoạt động vẫn giữ nguyên trong suốt quá trình thay đổi cấu hình, chẳng hạn như khi thực hiện thao tác xoay hoặc khi chuyển sang chế độ nhiều cửa sổ. Tuy nhiên, theo mặc định, hệ thống sẽ huỷ hoạt động khi xảy ra thay đổi về cấu hình như vậy, xoá sạch mọi trạng thái giao diện người dùng đã lưu trong thực thể của hoạt động đó. Để tìm hiểu thêm về cấu hình thiết bị, hãy xem trang tham chiếu Cấu hình. Lưu ý: Mặc dù không nên, nhưng bạn vẫn có thể ghi đè hành vi mặc định cho những thay đổi về cấu hình. Xem phần Tự xử lý thay đổi về cấu hình để biết thêm chi tiết.

Người dùng cũng mong muốn trạng thái giao diện người dùng hoạt động của bạn vẫn giữ nguyên nếu họ tạm thời chuyển sang một ứng dụng khác và sau đó quay lại ứng dụng của bạn. Ví dụ: Người dùng thực hiện việc tìm kiếm trong hoạt động tìm kiếm của bạn và sau đó nhấn nút màn hình chính hoặc trả lời một cuộc điện thoại – Khi họ quay lại hoạt động tìm kiếm, họ muốn thấy từ khoá và kết quả tìm kiếm vẫn ở đó giống như trước.

Trong trường hợp này, ứng dụng của bạn được đặt ở chế độ nền, hệ thống sẽ cố gắng hết sức để giữ cho quá trình xử lý ứng dụng luôn ở trong bộ nhớ. Tuy nhiên, hệ thống có thể huỷ bỏ quá trình xử lý ứng dụng trong khi người dùng đang thoát ra ngoài và tương tác với các ứng dụng khác. Trong trường hợp như vậy, hệ thống sẽ huỷ bỏ thực thể hoạt động và bất kỳ trạng thái nào được lưu trữ trong thực thể đó. Khi người dùng chạy lại ứng dụng, hoạt động sẽ bất ngờ ở trạng thái mới. Để tìm hiểu thêm về trường hợp dừng hoạt động, hãy xem phần Các quá trình xử lý và vòng đời ứng dụng.

Các tuỳ chọn duy trì trạng thái giao diện người dùng

Khi kỳ vọng của người dùng về trạng thái giao diện người dùng không khớp với hành vi mặc định của hệ thống, bạn phải lưu và khôi phục trạng thái giao diện người dùng để đảm bảo rằng quá trình huỷ bỏ do hệ thống bắt đầu là minh bạch đối với người dùng.

Mỗi tuỳ chọn để duy trì trạng thái giao diện người dùng sẽ khác nhau theo các phương diện ảnh hưởng đến trải nghiệm người dùng sau đây:

ViewModel Trạng thái của thực thể đã lưu Bộ nhớ liên tục
Vị trí lưu trữ trong bộ nhớ trong bộ nhớ trên đĩa hoặc mạng
Còn lại sau khi thay đổi cấu hình
Còn lại sau quá trình dừng hoạt động do hệ thống gây ra Không
Còn lại sau khi người dùng hoàn tất thao tác đóng hoạt động/onFinish() Không Không
Hạn mức dữ liệu các đối tượng phức tạp vẫn ổn, nhưng không gian bị giới hạn bởi bộ nhớ hiện có chỉ dành cho các loại nguyên hàm và các đối tượng nhỏ, đơn giản như Chuỗi chỉ bị giới hạn bởi dung lượng ổ đĩa hoặc chi phí/thời gian truy xuất từ tài nguyên mạng
Thời gian đọc/ghi nhanh (chỉ truy cập bộ nhớ) chậm (đòi hỏi chuyển đổi tuần tự/huỷ chuyển đổi tuần tự và truy cập vào ổ đĩa) chậm (đòi hỏi truy cập vào ổ đĩa hoặc giao dịch trên mạng)

Sử dụng ViewModel để xử lý các thay đổi về cấu hình

ViewModel là lớp lý tưởng để lưu trữ và quản lý dữ liệu liên quan đến giao diện người dùng trong khi người dùng đang chủ động sử dụng ứng dụng. ViewModel cho phép truy cập nhanh vào dữ liệu giao diện người dùng và giúp bạn khỏi phải tìm nạp lại dữ liệu từ mạng hoặc ổ đĩa khi xoay, đổi kích thước cửa sổ và các thay đổi về cấu hình thường gặp khác. Để tìm hiểu cách triển khai ViewModel, xem phần hướng dẫn ViewModel.

ViewModel giữ lại dữ liệu trong bộ nhớ, tức là truy xuất dữ liệu từ đây sẽ rẻ hơn so với dữ liệu từ ổ đĩa hoặc mạng. ViewModel được liên kết với một hoạt động (hoặc chủ sở hữu vòng đời khác) – lớp này ở trong bộ nhớ trong suốt quá trình thay đổi cấu hình và hệ thống sẽ tự động liên kết ViewModel với thực thể hoạt động mới là kết quả của quá trình thay đổi cấu hình.

Hệ thống sẽ tự động huỷ ViewModels khi người dùng của bạn rời khỏi hoạt động hoặc mảnh hoặc khi bạn gọi finish(), có nghĩa là, trong các trường hợp này, trạng thái sẽ bị xoá như người dùng muốn .

Không giống như trạng thái thực thể đã lưu, ViewModels sẽ bị huỷ trong trường hợp bị buộc tắt do hệ thống gây ra. Để tải lại dữ liệu sau khi hoạt động bị buộc tắt do hệ thống gây ra trong ViewModel, sử dụng API SavedStateHandle. Ngoài ra, nếu dữ liệu liên quan đến giao diện người dùng và không cần được giữ trong ViewModel, sử dụng onSaveInstanceState() trong hệ thống View hoặc rememberSaveable trong Jetpack Compose. Nếu dữ liệu là dữ liệu ứng dụng, tốt hơn là bạn nên lưu giữ dữ liệu vào ổ đĩa.

Nếu đã có sẵn giải pháp trong bộ nhớ để lưu trữ trạng thái giao diện người dùng sau khi thay đổi cấu hình, thì bạn có thể không cần phải sử dụng ViewModel.

Sử dụng trạng thái của thực thể đã lưu làm dự phòng để xử lý các trường hợp bị buộc tắt do hệ thống gây ra

Lệnh gọi lại onSaveInstanceState() trong hệ thống View, rememberSaveable trong Jetpack Compose và SavedStateHandle trong ViewModels lưu trữ dữ liệu cần để tải lại trạng thái của một trình điều khiển giao diện người dùng, chẳng hạn như một hoạt động hoặc một mảnh, nếu hệ thống huỷ bỏ và sau đó tạo lại trình điều khiển đó. Để tìm hiểu cách triển khai trạng thái thực thể đã lưu bằng onSaveInstanceState, xem mục Lưu và khôi phục trạng thái hoạt động trong phần hướng dẫn Vòng đời hoạt động.

Gói trạng thái thực thể đã lưu vẫn duy trì qua cả các lần thay đổi cấu hình và bị buộc tắt nhưng bị giới hạn bởi bộ nhớ và tốc độ, vì các API khác nhau chuyển đổi tuần tự dữ liệu vào đĩa. Quá trình chuyển đổi tuần tự có thể tốn nhiều bộ nhớ nếu các đối tượng được chuyển đổi rất phức tạp. Vì quá trình này xảy ra trên luồng chính trong quá trình thay đổi cấu hình, nên việc chuyển đổi tuần tự chạy trong thời gian dài có thể gây ra tình trạng sụt khung hình và gián đoạn hình ảnh.

Không sử dụng trạng thái bản sao đã lưu để lưu lượng dữ liệu lớn, chẳng hạn như bitmap hoặc cấu trúc dữ liệu phức tạp đòi hỏi quá trình chuyển đổi tuần tự hoặc huỷ chuyển đổi tuần tự dài. Thay vào đó, chỉ lưu trữ các loại nguyên hàm và đối tượng nhỏ, đơn giản như String. Do vậy, sử dụng trạng thái thực thể đã lưu để lưu trữ lượng dữ liệu tối thiểu cần thiết, chẳng hạn như mã nhận dạng, để tạo lại dữ liệu cần thiết nhằm khôi phục giao diện người dùng về trạng thái trước đó nếu các cơ chế cố định khác không hoạt động. Hầu hết các ứng dụng nên triển khai thao tác này để xử lý trường hợp bị buộc tắt do hệ thống gây ra.

Tuỳ thuộc vào các trường hợp sử dụng của ứng dụng, bạn có thể không cần sử dụng trạng thái của thực thể đã lưu. Ví dụ: Một trình duyệt có thể đưa người dùng quay lại đúng trang web mà họ đã xem trước khi thoát khỏi trình duyệt. Nếu hoạt động của bạn hành xử theo cách này, thì có thể bỏ qua việc sử dụng trạng thái của thực thể đã lưu và thay vào đó, duy trì mọi thứ một cách cục bộ.

Ngoài ra, khi bạn mở một hoạt động từ một ý định, hệ thống sẽ phân phối gói bổ sung tới hoạt động đó cả khi cấu hình thay đổi và khi hệ thống khôi phục hoạt động. Nếu một phần dữ liệu trạng thái giao diện người dùng, chẳng hạn như cụm từ tìm kiếm, được truyền vào dưới dạng một ý định bổ sung khi hoạt động được khởi chạy, thì bạn có thể sử dụng gói bổ sung thay vì gói trạng thái thực thể đã lưu. Để tìm hiểu thêm về ý định bổ sung, xem phần Ý định và bộ lọc ý định.

Trong cả hai trường hợp này, bạn vẫn nên sử dụng ViewModel để tránh lãng phí chu kỳ tải lại dữ liệu từ cơ sở dữ liệu trong quá trình thay đổi cấu hình.

Trong trường hợp dữ liệu giao diện người dùng cần duy trì mà đơn giản và gọn nhẹ, bạn có thể chỉ cần sử dụng các API trạng thái thực thể đã lưu để duy trì dữ liệu trạng thái của mình.

Chuyển sang trạng thái đã lưu bằng SavedStateRegistry

Bắt đầu với Mảnh 1.1.0 hoặc Hoạt động 1.0.0 phần phụ thuộc chuyển đổi, các trình điều khiển giao diện người dùng, chẳng hạn như Activity hoặc Fragment, triển khai SavedStateRegistryOwner và cung cấp SavedStateRegistry liên kết với trình điều khiển đó. SavedStateRegistry cho phép các thành phần móc vào trạng thái đã lưu của trình điều khiển giao diện người dùng để tiêu thụ hoặc đóng góp vào trạng thái đó. Ví dụ: Mô-đun trạng thái đã lưu cho ViewModel sử dụng SavedStateRegistry để tạo SavedStateHandle và cung cấp lớp đó cho các đối tượng ViewModel. Bạn có thể truy xuất SavedStateRegistry từ bên trong trình điều khiển giao diện người dùng bằng cách gọi getSavedStateRegistry().

Các thành phần đóng góp vào trạng thái đã lưu phải triển khai SavedStateRegistry.SavedStateProvider, nhằm xác định một phương thức duy nhất có tên là saveState(). Phương thức saveState() cho phép thành phần của bạn trả về một Bundle chứa bất kỳ trạng thái nào lưu trên thành phần đó. SavedStateRegistry gọi phương thức này trong giai đoạn lưu trạng thái trong vòng đời của trình điều khiển giao diện người dùng.

Kotlin

class SearchManager : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val QUERY = "query"
    }

    private val query: String? = null

    ...

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String QUERY = "query";
    private String query = null;
    ...

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }
}

Để đăng ký SavedStateProvider, hãy gọi registerSavedStateProvider() trên SavedStateRegistry, truyền khoá để liên kết với dữ liệu của nhà cung cấp cũng như với nhà cung cấp , Bạn có thể truy xuất dữ liệu đã lưu trước đây dành cho nhà cung cấp từ trạng thái đã lưu bằng cách gọi consumeRestoredStateForKey() trên SavedStateRegistry, truyền khoá liên kết với dữ liệu của nhà cung cấp.

Trong Activity hoặc Fragment, bạn có thể đăng ký SavedStateProvider trong onCreate() sau khi gọi super.onCreate(). Ngoài ra, bạn có thể đặt LifecycleObserver trên SavedStateRegistryOwner, nhằm triển khai LifecycleOwner và đăng ký SavedStateProvider khi sự kiện ON_CREATE xảy ra. Bằng cách sử dụng LifecycleObserver, bạn có thể tách thao tác đăng ký và truy xuất của trạng thái đã lưu trước đó từ chính SavedStateRegistryOwner.

Kotlin

class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val PROVIDER = "search_manager"
        private const val QUERY = "query"
    }

    private val query: String? = null

    init {
        // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE
        registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_CREATE) {
                val registry = registryOwner.savedStateRegistry

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this)

                // Get the previously saved state and restore it
                val state = registry.consumeRestoredStateForKey(PROVIDER)

                // Apply the previously saved state
                query = state?.getString(QUERY)
            }
        }
    }

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }

    ...
}

class SearchFragment : Fragment() {
    private var searchManager = SearchManager(this)
    ...
}

Java

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String PROVIDER = "search_manager";
    private static String QUERY = "query";
    private String query = null;

    public SearchManager(SavedStateRegistryOwner registryOwner) {
        registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
            if (event == Lifecycle.Event.ON_CREATE) {
                SavedStateRegistry registry = registryOwner.getSavedStateRegistry();

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this);

                // Get the previously saved state and restore it
                Bundle state = registry.consumeRestoredStateForKey(PROVIDER);

                // Apply the previously saved state
                if (state != null) {
                    query = state.getString(QUERY);
                }
            }
        });
    }

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }

    ...
}

class SearchFragment extends Fragment {
    private SearchManager searchManager = new SearchManager(this);
    ...
}

Sử dụng tính năng cố định cục bộ để xử lý trường hợp bị buộc tắt đối với dữ liệu lớn hoặc phức tạp

Bộ nhớ cục bộ ổn định, chẳng hạn như cơ sở dữ liệu hoặc các tuỳ chọn chung, sẽ vẫn còn lại cho đến khi ứng dụng của bạn được cài đặt trên thiết bị của người dùng (trừ khi người dùng xoá dữ liệu ứng dụng). Mặc dù bộ nhớ cục bộ như vậy vẫn sống sót sau hoạt động do hệ thống khởi tạo và sau khi ứng dụng bị buộc dừng, nhưng việc truy xuất có thể tốn kém vì hệ thống sẽ phải đọc dữ liệu từ bộ nhớ cục bộ trong bộ nhớ. Thông thường, bộ nhớ cục bộ ổn định này có thể đã là một phần của cấu trúc ứng dụng để lưu trữ tất cả dữ liệu mà bạn không muốn để mất nếu bạn mở và đóng hoạt động.

Cả ViewModel và trạng thái thực thể đã lưu đều không phải là giải pháp lưu trữ dài hạn và do đó, chúng không thể là giải pháp thay thế cho bộ nhớ cục bộ, chẳng hạn như cơ sở dữ liệu. Thay vào đó, bạn chỉ nên sử dụng các cơ chế này để lưu trữ ngắn hạn trạng thái giao diện người dùng tạm thời và sử dụng bộ nhớ ổn định cho dữ liệu ứng dụng khác. Xem phần Hướng dẫn cấu trúc ứng dụng để biết thêm thông tin chi tiết về cách tận dụng bộ nhớ cục bộ để duy trì dữ liệu mô hình ứng dụng lâu dài (ví dụ: Qua các lần khởi động lại thiết bị).

Quá trình quản lý trạng thái giao diện người dùng: Chia để trị

Bạn có thể lưu và khôi phục hiệu quả trạng thái giao diện người dùng bằng cách phân chia công việc cho các loại cơ chế cố định khác nhau. Trong hầu hết các trường hợp, mỗi cơ chế này nên lưu trữ một loại dữ liệu khác nhau được dùng trong hoạt động đó, dựa trên sự đánh đổi về độ phức tạp của dữ liệu, tốc độ truy cập và toàn thời gian

  • Tính năng cố định cục bộ: Lưu trữ tất cả dữ liệu ứng dụng mà bạn không muốn mất đi nếu mở và đóng hoạt động.
    • Ví dụ: Một tập hợp các đối tượng bài hát, có thể bao gồm các tệp âm thanh và siêu dữ liệu.
  • ViewModel: Lưu trữ trong bộ nhớ tất cả dữ liệu cần để hiển thị giao diện người dùng liên kết, trạng thái giao diện người dùng màn hình.
    • Ví dụ: Các đối tượng bài hát của tìm kiếm gần đây nhất và cụm từ tìm kiếm gần đây nhất.
  • Trạng thái của thực thể đã lưu: Lưu trữ một lượng dữ liệu nhỏ cần thiết để dễ dàng tải lại trạng thái giao diện người dùng nếu hệ thống dừng và rồi tạo lại giao diện người dùng. Thay vì lưu trữ các đối tượng phức tạp ở đây, duy trì các đối tượng phức tạp trong bộ nhớ cục bộ và lưu trữ một mã nhận dạng duy nhất cho các đối tượng này trong API trạng thái thực thể đã lưu.
    • Ví dụ: Lưu trữ cụm từ tìm kiếm gần đây nhất.

Ví dụ: Hãy cân nhắc một hoạt động cho phép bạn tìm kiếm trong thư viện bài hát. Dưới đây là cách xử lý các sự kiện khác nhau:

Khi người dùng thêm một bài hát, ViewModel ngay lập tức uỷ quyền duy trì cục bộ dữ liệu này. Nếu bài hát mới thêm này cần hiển thị trong giao diện người dùng, thì bạn cũng nên cập nhật dữ liệu trong đối tượng ViewModel để phản ánh việc thêm bài hát. Nhớ thực hiện tất cả thao tác chèn cơ sở dữ liệu vào luồng chính.

Khi người dùng tìm kiếm một bài hát, dù dữ liệu bài hát bạn tải từ cơ sở dữ liệu phức tạp đến đâu, thì dữ liệu đó phải được lưu trữ ngay lập tức trong đối tượng ViewModel như một phần của trạng thái giao diện người dùng màn hình.

Khi hoạt động chuyển sang trạng thái nền và hệ thống gọi các API trạng thái thực thể đã lưu, cụm từ tìm kiếm phải được lưu trữ trong trạng thái phiên bản đã lưu, trong trường hợp quy trình này được tạo lại. Do vẫn cần thông tin để tải dữ liệu ứng dụng tồn đã tại trong quy trình này, lưu trữ cụm từ tìm kiếm trong SavedStateHandle ViewModel. Đây là tất cả thông tin bạn cần để tải dữ liệu và đưa giao diện người dùng trở về trạng thái hiện tại.

Quá trình khôi phục các trạng thái phức tạp: Tập hợp các chi tiết

Khi người dùng quay lại hoạt động, có thể có hai trường hợp tạo lại hoạt động sau đây:

  • Hoạt động được tạo lại sau khi bị hệ thống dừng. Hệ thống sẽ lưu cụm từ tìm kiếm này trong một gói trạng thái thực thể đã lưu và giao diện người dùng phải truyền truy vấn đó đến ViewModel nếu không sử dụng SavedStateHandle. ViewModel thấy rằng không có kết quả tìm kiếm nào được lưu vào bộ nhớ đệm và uỷ quyền tải các kết quả tìm kiếm bằng cụm từ tìm kiếm nhất định.
  • Hoạt động được tạo sau khi thay đổi cấu hình. Do thực thể ViewModel chưa bị huỷ, nên ViewModel có tất cả thông tin trong bộ nhớ đệm và không cần truy vấn lại cơ sở dữ liệu.

Tài nguyên khác

Để tìm hiểu thêm về cách lưu các trạng thái giao diện người dùng, xem các tài nguyên sau.

Blog