Stabilitas dalam Compose

Compose menganggap jenis sebagai stabil atau tidak stabil. Suatu jenis dikatakan stabil jika tidak dapat diubah, atau jika Compose dapat mengetahui apakah nilainya telah berubah saat rekomposisi. Suatu jenis dikatakan tidak stabil jika Compose tidak dapat mengetahui apakah nilainya telah berubah saat rekomposisi.

Compose menggunakan stabilitas parameter composable untuk menentukan cara membandingkan input dan memutuskan apakah composable dapat melewati rekomposisi (dengan Mode skipping kuat diaktifkan secara default mulai di Kotlin 2.0.20):

  • Parameter stabil: Compose membandingkan parameter stabil menggunakan persamaan struktural (Object.equals()). Jika parameter stabil composable sama dengan nilai sebelumnya, Compose akan melewatinya.
  • Parameter tidak stabil: Dengan Mode skipping kuat diaktifkan (default di Kotlin 2.0.20 dan yang lebih tinggi), Compose membandingkan parameter tidak stabil menggunakan persamaan instance (===) dan melewati composable jika instance objek yang sama diteruskan. Pada versi sebelum Kotlin 2.0.20 (atau saat mode skipping kuat dinonaktifkan), Compose selalu merekomposisi composable dengan parameter tidak stabil saat induknya merekomposisi.

Jika aplikasi Anda sering mengalokasikan instance baru dari parameter yang tidak stabil—atau memaksakan pemeriksaan .equals() yang mahal pada koleksi besar dengan menganotasi model secara berlebihan—Anda mungkin mengamati rekomposisi yang tidak perlu atau overhead perbandingan.

Dokumen ini menjelaskan secara mendetail cara Compose menentukan stabilitas dan cara Anda dapat mengoptimalkannya untuk meningkatkan performa dan pengalaman pengguna secara keseluruhan.

Objek yang tidak dapat diubah

Cuplikan berikut menunjukkan prinsip umum di balik stabilitas dan rekomposisi.

Class Contact adalah class data yang tidak dapat diubah. Hal ini karena semua parameternya adalah primitif yang ditentukan dengan kata kunci val. Setelah membuat instance Contact, Anda tidak dapat mengubah nilai properti objek. Jika Anda mencoba melakukannya, Anda akan membuat objek baru.

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

Composable ContactRow memiliki parameter jenis Contact.

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

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

Pertimbangkan apa yang terjadi saat pengguna mengklik tombol pengalih dan status selected berubah:

  1. Compose mengevaluasi apakah harus merekomposisi kode di dalam ContactRow.
  2. Model melihat bahwa satu-satunya argumen untuk ContactDetails adalah jenis Contact.
  3. Karena Contact adalah class data yang tidak dapat diubah, Compose yakin bahwa tidak ada argumen untuk ContactDetails yang berubah.
  4. Oleh karena itu, Compose melewati ContactDetails dan tidak merekomposisinya.
  5. Di sisi lain, argumen untuk ToggleButton telah berubah, dan Compose merekomposisi komponen tersebut.

Objek yang dapat berubah

Meskipun contoh sebelumnya menggunakan objek yang tidak dapat diubah, Anda dapat membuat objek yang dapat diubah. Pertimbangkan cuplikan berikut:

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

Karena setiap parameter Contact kini berupa var, class tidak lagi tidak dapat diubah. Jika propertinya berubah, Compose tidak akan mengetahuinya. Hal ini karena Compose hanya melacak perubahan pada objek Status Compose.

Compose menganggap class tersebut tidak stabil. Dengan Mode lewati kuat (Kotlin 2.0.20+), Compose membandingkan Contact menggunakan kesamaan instance (===): memutasi contact.name di tempat tidak akan memicu rekomposisi, sementara meneruskan instance Contact yang baru dialokasikan dengan nilai yang identik akan tetap memaksa ContactDetails untuk merekomposisi (dan tanpa Mode lewati kuat, ContactDetails merekomposisi setiap kali selected berubah).

Implementasi di Compose

Meskipun tidak penting, ada baiknya untuk mempertimbangkan secara tepat cara Compose menentukan fungsi mana yang akan dilewati selama rekomposisi.

Saat compiler Compose berjalan di kode Anda, compiler akan menandai setiap fungsi dan jenis dengan salah satu dari beberapa tag. Tag ini mencerminkan cara Compose menangani fungsi atau jenis selama rekomposisi.

Fungsi

Compose dapat menandai fungsi sebagai skippable atau restartable. Perhatikan bahwa fungsi ini dapat menandai fungsi sebagai salah satu, keduanya, atau tidak satu pun dari hal berikut:

  • Dapat dilewati: Jika compiler menandai composable sebagai dapat dilewati, Compose dapat melewatinya selama rekomposisi jika semua argumennya sama dengan nilai sebelumnya. (Dengan Mode skipping kuat di Kotlin 2.0.20+, semua composable yang dapat dimulai ulang otomatis dapat dilewati.)
  • Dapat dimulai ulang: Composable yang dapat dimulai ulang berfungsi sebagai "cakupan" tempat rekomposisi dapat dimulai. Dengan kata lain, fungsi dapat menjadi titik entri tempat Compose dapat mulai mengeksekusi ulang kode untuk rekomposisi setelah perubahan status.

Jenis

Compose menandai jenis sebagai tidak dapat diubah atau stabil. Setiap jenis adalah salah satu dari berikut:

  • Tidak dapat diubah: Compose menandai jenis sebagai tidak dapat diubah jika nilai propertinya tidak pernah berubah dan semua metodenya transparan secara referensial.
    • Perhatikan bahwa semua jenis primitif ditandai sebagai tidak dapat diubah. Hal ini mencakup String, Int, dan Float.
  • Stabil: Menunjukkan jenis yang propertinya dapat berubah setelah konstruksi. Jika dan saat properti tersebut berubah selama runtime, Compose akan mengetahui perubahan tersebut.

Stabilitas debug

Jika aplikasi Anda merekomposisi composable yang parameternya tidak berubah, pertama-tama periksa apakah instance baru dari jenis yang tidak stabil dialokasikan pada setiap rekomposisi (atau, jika mode skipping Kuat dinonaktifkan, apakah composable memiliki parameter dengan properti var atau properti val dari jenis yang tidak stabil).

Untuk mengetahui informasi mendetail tentang cara mendiagnosis masalah kompleks terkait stabilitas di Compose, lihat panduan Men-debug stabilitas.

Memperbaiki masalah stabilitas

Untuk mengetahui informasi tentang cara menstabilkan penerapan Compose, lihat panduan Memperbaiki masalah stabilitas.

Ringkasan

Secara keseluruhan, Anda harus memperhatikan poin-poin berikut:

  • Parameter: Compose menentukan stabilitas setiap parameter composable Anda untuk memutuskan apakah akan membandingkannya menggunakan persamaan struktural (.equals()) atau persamaan instance (===) selama rekomposisi.
  • Perbaikan langsung: Jika Anda melihat composable tidak dilewati dan menyebabkan masalah performa, periksa apakah instance baru parameter yang tidak stabil dibuat ulang pada setiap penerusan atau apakah properti var sedang digunakan, bukan State.
  • Laporan compiler: Anda dapat menggunakan laporan compiler untuk menentukan stabilitas yang disimpulkan tentang class Anda.
  • Koleksi: Compose menganggap antarmuka koleksi standar (List, Set, dan Map) tidak stabil karena implementasi yang mendasarinya dapat berubah. Dengan Mode lewati kuat (diaktifkan secara default di Kotlin 2.0.20+), koleksi yang tidak stabil tetap melewati rekomposisi menggunakan kesamaan instance O(1) cepat (===) saat referensi koleksi tidak berubah. Hindari mengonversi koleksi besar ke Koleksi imutable Kotlinx atau mengannotasi class yang menyimpan koleksi dengan @Immutable atau @Stable kecuali persamaan struktural (O(N) .equals()) secara khusus diperlukan, karena membandingkan setiap elemen pada setiap rekomposisi dapat lebih mahal daripada rekomposisi itu sendiri.
  • Modul lain: Compose menganggap class dari modul tempat compiler Compose tidak berjalan sebagai tidak stabil (dibandingkan menggunakan === dalam mode skipping Kuat). Jika sumber data sering membuat ulang model kecil dan datar dari modul non-Compose dengan nilai yang identik, Anda dapat mengonfigurasi file konfigurasi stabilitas atau menggunakan @Stable atau @Immutable jika .equals() murah.

Bacaan lebih lanjut