Độ ổn định trong Compose

Compose xem các loại là ổn định hoặc không ổn định. Một loại được coi là ổn định nếu đó là loại bất biến hoặc nếu Compose có thể xác định liệu giá trị của thành phần đó có thay đổi giữa các lần kết hợp lại hay không. Một loại được coi là không ổn định nếu Compose không thể xác định liệu giá trị của thành phần đó có thay đổi giữa các lần kết hợp lại hay không.

Compose sử dụng độ ổn định của các tham số của một thành phần kết hợp để xác định cách so sánh dữ liệu đầu vào và quyết định xem có thể bỏ qua thành phần kết hợp đó trong quá trình tái cấu trúc hay không (với Chế độ bỏ qua mạnh được bật theo mặc định kể từ Kotlin 2.0.20):

  • Tham số ổn định: Compose so sánh các tham số ổn định bằng cách sử dụng tính bằng nhau về cấu trúc (Object.equals()). Nếu các tham số ổn định của một thành phần kết hợp bằng với giá trị trước đó, thì Compose sẽ bỏ qua thành phần kết hợp đó.
  • Tham số không ổn định: Khi bật Chế độ bỏ qua hữu hiệu (mặc định trong Kotlin 2.0.20 trở lên), Compose sẽ so sánh các tham số không ổn định bằng cách sử dụng tính bằng nhau của thực thể (===) và bỏ qua thành phần kết hợp nếu các thực thể đối tượng giống nhau được truyền. Trong các phiên bản trước Kotlin 2.0.20 (hoặc khi chế độ bỏ qua hữu hiệu bị tắt), Compose luôn kết hợp lại một thành phần kết hợp có các tham số không ổn định khi thành phần mẹ của thành phần kết hợp đó kết hợp lại.

Nếu ứng dụng của bạn thường xuyên phân bổ các thực thể mới của tham số không ổn định hoặc buộc phải kiểm tra .equals() tốn kém trên các tập hợp lớn bằng cách chú thích quá mức cho các mô hình, thì bạn có thể thấy các hoạt động kết hợp lại không cần thiết hoặc hao tổn tài nguyên khi so sánh.

Tài liệu này trình bày chi tiết cách Compose xác định độ ổn định và cách bạn có thể tối ưu hoá độ ổn định để cải thiện hiệu suất và trải nghiệm tổng thể của người dùng.

Đối tượng không thể thay đổi

Các đoạn mã sau đây minh hoạ các nguyên tắc chung về độ ổn định và quá trình kết hợp lại.

Lớp Contact là một lớp dữ liệu không thể thay đổi. Điều này là do tất cả các tham số của hàm này đều là các dữ liệu gốc được xác định bằng từ khoá val. Sau khi tạo một thực thể của Contact, bạn không thể thay đổi giá trị thuộc tính của đối tượng. Nếu cố gắng làm như vậy, bạn sẽ tạo một đối tượng mới.

data class Contact(val name: String, val number: String)

Thành phần kết hợp ContactRow có một tham số thuộc loại Contact.

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

Hãy xem xét điều gì sẽ xảy ra khi người dùng nhấp vào nút bật tắt và trạng thái selected thay đổi:

  1. Compose đánh giá xem có nên kết hợp lại mã bên trong ContactRow hay không.
  2. Trình biên dịch thấy rằng đối số duy nhất cho ContactDetails thuộc loại Contact.
  3. Vì Contact là một lớp dữ liệu không thể thay đổi, nên Compose chắc chắn rằng không có đối số nào cho ContactDetails đã thay đổi.
  4. Do đó, Compose sẽ bỏ qua ContactDetails và không kết hợp lại.
  5. Mặt khác, các đối số cho ToggleButton đã thay đổi và Compose kết hợp lại thành phần đó.

Đối tượng có thể thay đổi

Mặc dù ví dụ trước sử dụng một đối tượng không thể thay đổi, nhưng bạn có thể tạo một đối tượng có thể thay đổi. Hãy xem xét đoạn mã sau:

data class Contact(var name: String, var number: String)

Vì mỗi tham số của Contact hiện là một var, nên lớp này không còn bất biến nữa. Nếu các thuộc tính của lớp này thay đổi, Compose sẽ không nhận biết được. Điều này là do Compose chỉ theo dõi các thay đổi đối với đối tượng State của Compose.

Compose coi lớp như vậy là không ổn định. Với Chế độ bỏ qua mạnh (Kotlin 2.0.20 trở lên), Compose so sánh Contact bằng cách sử dụng tính bằng nhau của thực thể (===): việc thay đổi contact.name tại chỗ sẽ không kích hoạt quá trình kết hợp lại, trong khi việc truyền một thực thể Contact mới được phân bổ có các giá trị giống hệt nhau vẫn sẽ buộc ContactDetails kết hợp lại (và nếu không có Chế độ bỏ qua mạnh, ContactDetails sẽ kết hợp lại mỗi khi selected thay đổi).

Triển khai trong Compose

Bạn nên xem xét chính xác cách Compose xác định hàm nào cần bỏ qua trong quá trình kết hợp lại, mặc dù điều này không quan trọng.

Khi chạy trên mã của bạn, trình biên dịch Compose sẽ đánh dấu từng hàm và loại bằng một trong số các thẻ. Các thẻ này phản ánh cách Compose xử lý hàm hoặc loại trong quá trình kết hợp lại.

Hàm

Compose có thể đánh dấu các hàm là skippable hoặc restartable. Xin lưu ý rằng trình biên dịch có thể đánh dấu một hàm là một, cả hai hoặc không có hàm nào trong số này:

  • Có thể bỏ qua: Nếu trình biên dịch đánh dấu một thành phần kết hợp là có thể bỏ qua, thì Compose có thể bỏ qua thành phần đó trong quá trình kết hợp lại nếu tất cả đối số của thành phần đó đều bằng với giá trị trước đó. (Với Chế độ bỏ qua hữu hiệu trong Kotlin 2.0.20 trở lên, tất cả thành phần kết hợp có thể khởi động lại đều tự động có thể bỏ qua.)
  • Có thể khởi động lại: composable có thể khởi động lại đóng vai trò là "phạm vi" nơi quá trình recomposition có thể bắt đầu. Nói cách khác, hàm có thể là điểm truy cập mà Compose có thể bắt đầu thực thi lại mã để kết hợp lại sau khi trạng thái thay đổi.

Loại

Compose đánh dấu các loại là không thể thay đổi hoặc ổn định. Mỗi loại là một trong hai loại sau:

  • Không thể thay đổi: Compose đánh dấu một loại là không thể thay đổi nếu giá trị của các thuộc tính không bao giờ thay đổi và tất cả phương thức đều minh bạch về mặt tham chiếu.
    • Xin lưu ý rằng tất cả các kiểu dữ liệu gốc đều được đánh dấu là không thể thay đổi. Các phương thức này bao gồm String, Int và Float.
  • Ổn định: Cho biết một kiểu có các thuộc tính có thể thay đổi sau khi tạo. Nếu và khi các thuộc tính đó thay đổi trong thời gian chạy, Compose sẽ nhận biết được những thay đổi đó.

Gỡ lỗi về độ ổn định

Nếu ứng dụng của bạn đang recomposition một composable có các tham số không thay đổi, trước tiên, hãy kiểm tra xem các thực thể mới của loại không ổn định có được phân bổ trên mỗi lần recomposition hay không (hoặc nếu Chế độ bỏ qua mạnh bị tắt, hãy kiểm tra xem composable có các tham số có thuộc tính var hoặc thuộc tính val của một loại không ổn định hay không).

Để biết thông tin chi tiết về cách chẩn đoán các vấn đề phức tạp liên quan đến độ ổn định trong Compose, hãy xem hướng dẫn Gỡ lỗi về độ ổn định.

Khắc phục vấn đề về độ ổn định

Để biết thông tin về cách làm cho quá trình triển khai Compose trở nên ổn định, hãy xem hướng dẫn Khắc phục sự cố về tính ổn định.

Tóm tắt

Nhìn chung, bạn nên lưu ý những điểm sau:

  • Tham số: Compose xác định độ ổn định của từng tham số trong các thành phần kết hợp để quyết định xem có nên so sánh các tham số đó bằng cách sử dụng tính cân bằng về cấu trúc (.equals()) hay tính cân bằng về thực thể (===) trong quá trình kết hợp lại hay không.
  • Cách khắc phục tức thì: Nếu bạn nhận thấy composable của mình không bị bỏ qua và gây ra vấn đề về hiệu suất, hãy kiểm tra xem các thực thể mới của tham số không ổn định có đang được tạo lại trên mỗi lượt truyền hay không hoặc liệu các thuộc tính var có đang được sử dụng thay vì State hay không.
  • Báo cáo trình biên dịch: Bạn có thể sử dụng báo cáo trình biên dịch để xác định độ ổn định được suy luận về các lớp của bạn.
  • Tập hợp: Compose coi các giao diện tập hợp chuẩn (List, Set và Map) là không ổn định vì các phương thức triển khai cơ bản của chúng có thể thay đổi được. Với Chế độ bỏ qua mạnh (được bật theo mặc định trong Kotlin 2.0.20 trở lên), các tập hợp không ổn định vẫn bỏ qua quá trình kết hợp lại bằng cách sử dụng tính bằng nhau của thực thể (===) khi tệp đối chiếu của tập hợp không thay đổi. Hãy thận trọng khi thay thế các tập hợp tiêu chuẩn bằng tập hợp không thể thay đổi Kotlinx hoặc chú thích các lớp chứa tập hợp bằng @Immutable hoặc @Stable, vì việc so sánh mọi phần tử với .equals() trên mỗi lượt truyền tái cấu trúc có thể tốn kém hơn so với việc tái cấu trúc.
  • Các mô-đun khác: Compose coi các lớp trong mô-đun mà trình biên dịch Compose không chạy là không ổn định (được so sánh bằng === ở Chế độ bỏ qua mạnh). Nếu một nguồn dữ liệu thường xuyên tạo lại các mô hình nhỏ, phẳng từ các mô-đun không phải Compose có giá trị giống hệt nhau, thì bạn có thể định cấu hình tệp cấu hình độ ổn định hoặc sử dụng @Stable hoặc @Immutable khi .equals() không tốn kém.

Tài liệu đọc thêm