Memperbaiki masalah stabilitas

Saat menghadapi class yang tidak stabil yang menyebabkan masalah performa, Anda harus membuatnya stabil. Dokumen ini menguraikan beberapa teknik yang dapat Anda gunakan untuk melakukannya.

Aktifkan pengabaian yang kuat

Pertama-tama, Anda harus mencoba mengaktifkan mode lewati yang kuat. Mode skipping yang kuat memungkinkan composable dengan parameter yang tidak stabil dilewati dan merupakan metode termudah untuk memperbaiki masalah performa yang disebabkan oleh stabilitas.

Lihat Lewati yang kuat untuk mengetahui informasi selengkapnya.

Membuat class tidak dapat diubah

Anda juga dapat mencoba membuat class yang tidak stabil sepenuhnya tidak dapat diubah.

  • Tidak dapat diubah: Menunjukkan jenis yang nilai propertinya tidak pernah dapat berubah setelah instance jenis tersebut dibuat, dan semua metode transparan secara referensi.
    • Pastikan semua properti class adalah val, bukan var, dan dari jenis yang tidak dapat diubah.
    • Jenis primitif seperti String, Int, dan Float selalu tidak dapat diubah.
    • Jika tidak memungkinkan, Anda harus menggunakan status Compose untuk properti yang dapat diubah.
  • Stabil: Menunjukkan jenis yang dapat diubah. Runtime Compose tidak mengetahui jika dan kapan salah satu properti publik atau perilaku metode jenis tersebut akan menghasilkan hasil yang berbeda dari pemanggilan sebelumnya.

Koleksi yang tidak dapat diubah

Alasan umum mengapa Compose menganggap class tidak stabil adalah koleksi. Seperti yang disebutkan di halaman Diagnosa masalah stabilitas, compiler Compose tidak dapat sepenuhnya yakin bahwa koleksi seperti List, Map, dan Set benar-benar tidak dapat diubah sehingga menandainya sebagai tidak stabil.

Untuk mengatasi hal ini, Anda dapat menggunakan koleksi yang tidak dapat diubah. Compiler Compose menyertakan dukungan untuk Koleksi Immutable Kotlinx. Koleksi ini dijamin tidak dapat diubah, dan compiler Compose memperlakukannya sebagai tidak dapat diubah. Library ini masih dalam versi alfa, jadi kemungkinan akan ada perubahan pada API-nya.

Pertimbangkan kembali class yang tidak stabil ini dari panduan Diagnosa masalah stabilitas:

unstable class Snack {
  …
  unstable val tags: Set<String>
  …
}

Anda dapat membuat tags stabil menggunakan koleksi yang tidak dapat diubah. Di class, ubah jenis tags menjadi ImmutableSet<String>:

data class Snack{
    …
    val tags: ImmutableSet<String> = persistentSetOf()
    …
}

Setelah melakukannya, semua parameter class tidak dapat diubah, dan compiler Compose akan menandai class sebagai stabil.

Anotasi dengan Stable atau Immutable

Jalur yang memungkinkan untuk menyelesaikan masalah stabilitas adalah dengan menganotasi class yang tidak stabil dengan @Stable atau @Immutable.

Menganotasi class menggantikan apa yang akan disimpulkan tentang class Anda oleh compiler. Hal ini mirip dengan operator !! di Kotlin. Anda harus sangat berhati-hati dalam menggunakan anotasi ini. Mengganti perilaku compiler dapat menyebabkan bug yang tidak terduga, seperti composable yang tidak merekomposisi saat Anda mengharapkannya.

Jika memungkinkan untuk membuat class Anda stabil tanpa anotasi, Anda harus berupaya mencapai stabilitas dengan cara tersebut.

Cuplikan berikut memberikan contoh minimal dari class data yang dianotasi sebagai tidak dapat diubah:

@Immutable
data class Snack(
…
)

Baik Anda menggunakan anotasi @Immutable atau @Stable, compiler Compose akan menandai class Snack sebagai stabil.

Class yang dianotasi dalam koleksi

Pertimbangkan composable yang menyertakan parameter jenis List<Snack>:

restartable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  …
  unstable snacks: List<Snack>
  …
)

Meskipun Anda menganotasi Snack dengan @Immutable, compiler Compose masih menandai parameter snacks di HighlightedSnacks sebagai tidak stabil.

Parameter menghadapi masalah yang sama seperti class dalam hal jenis koleksi, compiler Compose selalu menandai parameter jenis List sebagai tidak stabil, meskipun merupakan koleksi jenis yang stabil.

Anda tidak dapat menandai setiap parameter sebagai stabil, atau menganotasi composable agar selalu dapat dilewati. Ada beberapa jalur ke depan.

Ada beberapa cara untuk mengatasi masalah koleksi yang tidak stabil. Subbagian berikut menguraikan berbagai pendekatan tersebut.

File konfigurasi

Jika Anda bersedia mematuhi kontrak stabilitas di codebase, Anda dapat memilih untuk mempertimbangkan koleksi Kotlin sebagai stabil dengan menambahkan kotlin.collections.* ke file konfigurasi stabilitas.

Koleksi yang tidak dapat diubah

Untuk keamanan waktu kompilasi dari sifat tidak dapat diubah, Anda dapat menggunakan koleksi kotlinx immutabel, bukan List.

@Composable
private fun HighlightedSnacks(
    …
    snacks: ImmutableList<Snack>,
    …
)

Wrapper

Jika tidak dapat menggunakan koleksi yang tidak dapat diubah, Anda dapat membuat koleksi sendiri. Untuk melakukannya, gabungkan List dalam class stabil yang dianotasi. Wrapper generik mungkin merupakan pilihan terbaik untuk ini, bergantung pada persyaratan Anda.

@Immutable
data class SnackCollection(
   val snacks: List<Snack>
)

Kemudian, Anda dapat menggunakannya sebagai jenis parameter dalam composable.

@Composable
private fun HighlightedSnacks(
    index: Int,
    snacks: SnackCollection,
    onSnackClick: (Long) -> Unit,
    modifier: Modifier = Modifier
)

Solusi

Setelah melakukan salah satu pendekatan ini, compiler Compose kini menandai Composable HighlightedSnacks sebagai skippable dan restartable.

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun HighlightedSnacks(
  stable index: Int
  stable snacks: ImmutableList<Snack>
  stable onSnackClick: Function1<Long, Unit>
  stable modifier: Modifier? = @static Companion
)

Selama rekomposisi, Compose kini dapat melewati HighlightedSnacks jika tidak ada input yang berubah.

File konfigurasi stabilitas

Mulai dari Compose Compiler 1.5.5, file konfigurasi class yang dianggap stabil dapat disediakan pada waktu kompilasi. Hal ini memungkinkan untuk mempertimbangkan class yang tidak Anda kontrol, seperti class library standar seperti LocalDateTime, sebagai stabil.

File konfigurasi adalah file teks biasa dengan satu class per baris. Komentar, karakter pengganti tunggal, dan ganda didukung. Contoh konfigurasi:

// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider my datalayer stable
com.datalayer.*
// Consider my datalayer and all submodules stable
com.datalayer.**
// Consider my generic type stable based off it's first type parameter only
com.example.GenericClass<*,_>

Untuk mengaktifkan fitur ini, teruskan jalur file konfigurasi ke composeCompiler blok opsi pada konfigurasi plugin Gradle compiler Compose.

composeCompiler {
  stabilityConfigurationFile = rootProject.layout.projectDirectory.file("stability_config.conf")
}

Karena compiler Compose berjalan pada setiap modul dalam project secara terpisah, Anda dapat memberikan konfigurasi yang berbeda ke modul yang berbeda jika diperlukan. Atau, miliki satu konfigurasi di level root project dan teruskan jalur tersebut ke setiap modul.

Beberapa modul

Masalah umum lainnya terkait arsitektur multi-modul. Compiler Compose hanya dapat menyimpulkan apakah class stabil jika semua jenis non-primitif yang direferensikannya ditandai secara eksplisit sebagai stabil atau dalam modul yang juga dibuat dengan compiler Compose.

Jika lapisan data Anda berada dalam modul terpisah dari lapisan UI, yang merupakan pendekatan yang direkomendasikan, ini mungkin menjadi masalah yang Anda temui.

Solusi

Untuk mengatasi masalah ini, Anda dapat menggunakan salah satu pendekatan berikut:

  1. Tambahkan class ke file konfigurasi Compiler Anda.
  2. Aktifkan compiler Compose di modul lapisan data, atau beri tag pada class dengan @Stable atau @Immutable jika sesuai.
    • Hal ini melibatkan penambahan dependensi Compose ke lapisan data Anda. Namun, ini hanya dependensi untuk runtime Compose, bukan untuk Compose-UI.
  3. Dalam modul UI, gabungkan class lapisan data dalam class wrapper khusus UI.

Masalah yang sama juga terjadi saat menggunakan library eksternal jika library tersebut tidak menggunakan compiler Compose.

Tidak semua composable boleh dilewati

Saat berupaya memperbaiki masalah stabilitas, Anda tidak boleh mencoba membuat setiap composable dapat dilewati. Mencoba melakukannya dapat menyebabkan pengoptimalan dini yang menimbulkan lebih banyak masalah daripada perbaikannya.

Ada banyak situasi ketika iklan yang dapat dilewati tidak memiliki manfaat nyata dan dapat menyebabkan sulitnya mempertahankan kode. Contoh:

  • Composable yang tidak sering direkomposisi, atau sama sekali tidak direkomposisi.
  • Composable yang hanya memanggil composable yang dapat dilewati.
  • Composable dengan sejumlah besar parameter dengan implementasi sama dengan yang mahal. Dalam hal ini, biaya pemeriksaan apakah parameter apa pun telah berubah dapat lebih besar daripada biaya rekomposisi murah.

Jika composable dapat dilewati, composable tersebut akan menambahkan overhead kecil yang mungkin tidak sepadan. Anda bahkan dapat menganotasi composable agar tidak dapat dimulai ulang jika Anda memutuskan bahwa dapat dimulai ulang akan menimbulkan overhead yang lebih besar daripada manfaatnya.