Saat dihadapkan dengan class yang tidak stabil yang menyebabkan masalah performa, Anda harus membuatnya stabil. Dokumen ini menguraikan beberapa teknik yang dapat Anda gunakan untuk melakukannya.
Membuat class tidak dapat diubah
Pertama-tama, Anda harus mencoba membuat class yang tidak stabil sepenuhnya tidak dapat diubah.
- Tidak dapat diubah: Menunjukkan jenis yang nilai propertinya tidak dapat
berubah setelah instance jenis tersebut dibuat, dan semua metode
transparan secara referensi.
- Pastikan semua properti class adalah
val
, bukanvar
, dan jenis yang tidak dapat diubah. - Jenis primitif seperti
String, Int
danFloat
selalu tidak dapat diubah. - Jika tidak memungkinkan, Anda harus menggunakan status Compose untuk properti apa pun yang dapat berubah.
- Pastikan semua properti class adalah
- Stabil: Menunjukkan jenis yang dapat diubah. Runtime Compose tidak mengetahui apakah dan kapan salah satu properti publik atau perilaku metode jenis akan memberikan hasil yang berbeda dari pemanggilan sebelumnya.
Koleksi yang tidak dapat diubah
Alasan umum mengapa Compose menganggap class tidak stabil adalah koleksi. Seperti yang dinyatakan
di halaman Diagnosis masalah stabilitas, compiler Compose
tidak dapat sepenuhnya memastikan 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 Kotlinx yang Tidak Dapat Diubah. Koleksi ini dijamin tidak dapat diubah, dan compiler Compose memperlakukannya sebagaimana mestinya. Library ini masih dalam versi alfa, jadi mungkin akan ada perubahan pada API-nya.
Pertimbangkan lagi class yang tidak stabil ini dari panduan Mendiagnosis 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 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 akan menggantikan apa yang akan disimpulkan class Anda oleh compiler. Operator ini mirip dengan
operator !!
di Kotlin. Anda harus sangat berhati-hati
dalam menggunakan anotasi ini. Mengganti perilaku compiler
dapat menyebabkan Anda mengalami bug yang tidak terduga, seperti composable tidak merekomposisi
saat Anda mengharapkannya.
Jika memungkinkan untuk membuat class menjadi stabil tanpa anotasi, Anda harus berusaha untuk mencapai stabilitas dengan cara tersebut.
Cuplikan berikut memberikan contoh minimal class data yang dianotasikan sebagai tidak dapat diubah:
@Immutable
data class Snack(
…
)
Baik Anda menggunakan anotasi @Immutable
maupun @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 tetap menandai
parameter snacks
di HighlightedSnacks
sebagai tidak stabil.
Parameter menghadapi masalah yang sama dengan class terkait jenis koleksi,
compiler Compose selalu menandai parameter jenis List
sebagai tidak stabil, meskipun
merupakan kumpulan jenis stabil.
Anda tidak dapat menandai parameter individual sebagai stabil, dan juga tidak dapat menganotasi composable agar selalu dapat dilewati. Ada beberapa jalur ke depan.
Ada beberapa cara untuk mengatasi masalah koleksi yang tidak stabil. Sub-bagian berikut menguraikan pendekatan yang berbeda ini.
File konfigurasi
Jika Anda senang mematuhi kontrak stabilitas di codebase Anda, maka
Anda dapat memilih untuk mempertimbangkan koleksi Kotlin sebagai stabil dengan menambahkan
kotlin.collections.*
ke
file konfigurasi stabilitas Anda.
Pengumpulan yang tidak dapat diubah
Untuk keamanan waktu kompilasi yang tidak dapat diubah, Anda dapat
menggunakan koleksi tetap kotlinx, bukan List
.
@Composable
private fun HighlightedSnacks(
…
snacks: ImmutableList<Snack>,
…
)
Wrapper
Jika tidak dapat menggunakan koleksi yang tidak dapat diubah, Anda dapat membuatnya sendiri. Untuk melakukannya,
gabungkan List
dalam class stabil yang dianotasi. Wrapper umum kemungkinan merupakan
pilihan terbaik untuk hal ini, bergantung pada persyaratan Anda.
@Immutable
data class SnackCollection(
val snacks: List<Snack>
)
Anda kemudian dapat menggunakannya sebagai jenis parameter dalam composable Anda.
@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 sekarang dapat melewati HighlightedSnacks
jika tidak ada
inputnya 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, misalnya 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 ditampilkan di bawah ini:
// Consider LocalDateTime stable
java.time.LocalDateTime
// Consider kotlin collections stable
kotlin.collections.*
// 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 opsi compiler Compose.
Groovy
kotlinOptions {
freeCompilerArgs += [
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
project.absolutePath + "/compose_compiler_config.conf"
]
}
Kotlin
kotlinOptions {
freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:stabilityConfigurationPath=" +
"${project.absolutePath}/compose_compiler_config.conf"
)
}
Saat 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 tingkat root project Anda dan teruskan jalur tersebut ke setiap modul.
Beberapa modul
Masalah umum lainnya melibatkan 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, hal ini mungkin menjadi masalah yang Anda temui.
Solusi
Untuk mengatasi masalah ini, Anda dapat melakukan salah satu pendekatan berikut:
- Tambahkan class ke file konfigurasi Compiler.
- Aktifkan compiler Compose di modul lapisan data, atau beri tag class
dengan
@Stable
atau@Immutable
jika sesuai.- Hal ini melibatkan penambahan dependensi Compose ke lapisan data Anda. Namun,
ini hanyalah dependensi untuk runtime Compose, bukan untuk
Compose-UI
.
- Hal ini melibatkan penambahan dependensi Compose ke lapisan data Anda. Namun,
ini hanyalah dependensi untuk runtime Compose, bukan untuk
- 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 harus dapat dilewati
Saat berupaya memperbaiki masalah stabilitas, Anda tidak boleh mencoba membuat setiap composable dapat dilewati. Jika Anda mencoba melakukannya, pengoptimalan prematur akan menimbulkan lebih banyak masalah daripada yang dapat diperbaiki.
Ada banyak situasi saat iklan yang dapat dilewati tidak memiliki manfaat nyata dan dapat menyebabkan kesulitan dalam mempertahankan kode. Contoh:
- Composable yang tidak sering direkomposisi, atau tidak sama sekali.
- Composable yang dengan sendirinya hanya memanggil composable yang dapat dilewati.
- Composable dengan sejumlah besar parameter dengan implementasi yang mahal. Dalam hal ini, biaya pemeriksaan apakah parameter telah berubah dapat lebih besar daripada biaya rekomposisi yang murah.
Jika dapat dilewati, composable akan menambahkan overhead kecil yang mungkin tidak bermanfaat. Anda bahkan dapat menganotasi composable agar tidak dapat dimulai ulang jika Anda menentukan bahwa tindakan yang dapat dimulai ulang memerlukan overhead yang lebih besar daripada nilainya.