1. Sebelum memulai
Dalam codelab ini, Anda akan mempelajari cara meningkatkan performa runtime aplikasi Compose. Anda akan mengikuti pendekatan ilmiah untuk mengukur, men-debug, dan mengoptimalkan performa. Anda akan menyelidiki beberapa masalah performa dengan pelacakan sistem dan mengubah kode runtime dengan performa kurang baik di aplikasi contoh berisi beberapa layar yang mewakili tugas berbeda. Masing-masing layar dibangun berbeda dan mencakup hal berikut:
- Layar pertama adalah daftar dua kolom dengan item gambar dan beberapa tag di atas item. Di sini, Anda akan mengoptimalkan composable berat.
- Layar kedua dan ketiga berisi status yang sering direkomposisi. Di sini, Anda akan menghapus rekomposisi yang tidak perlu untuk mengoptimalkan performa.
- Layar terakhir berisi item yang tidak stabil. Di sini, Anda akan menstabilkan item dengan berbagai teknik.
Prasyarat
- Pengetahuan mengenai cara membangun aplikasi Compose.
- Pemahaman dasar tentang cara menguji atau menjalankan macrobenchmark.
Yang Anda pelajari
- Cara menunjukkan masalah performa dengan pelacakan sistem dan pelacakan komposisi.
- Cara menulis aplikasi Compose berperforma tinggi yang dirender dengan lancar.
Yang Anda perlukan
- Versi stabil Android Studio terbaru
- Perangkat Android fisik dengan Android 6 (level API 23) atau yang lebih baru
2. Memulai persiapan
Untuk memulai, ikuti langkah-langkah ini:
- Buat clone repositori GitHub:
$ git clone https://github.com/android/codelab-android-compose.git
Atau, Anda dapat mendownload repositori sebagai file ZIP:
- Buka project
PerformanceCodelab
yang berisi cabang berikut:
main
: Berisi kode awal untuk project ini, yang dapat Anda ubah untuk menyelesaikan codelab.end
: Berisi kode solusi untuk codelab ini.
Sebaiknya Anda memulai dengan cabang main
dan mengikuti codelab langkah demi langkah sesuai kemampuan Anda.
- Jika Anda ingin melihat kode solusi, jalankan perintah ini:
$ git clone -b end https://github.com/android/codelab-android-compose.git
Selain itu, Anda dapat mendownload kode solusi:
Opsional: Rekaman aktivitas sistem yang digunakan dalam codelab ini
Anda akan menjalankan beberapa benchmark yang merekam aktivitas sistem selama codelab.
Jika Anda tidak dapat menjalankan benchmark ini, berikut daftar rekaman aktivitas sistem yang dapat Anda download:
3. Pendekatan untuk memperbaiki masalah performa
Menemukan UI yang lambat dan berperforma kurang baik dapat dilakukan hanya dengan melihat sekilas dan menjelajahi aplikasi. Namun, sebelum mulai memperbaiki kode berdasarkan asumsi, Anda harus mengukur performa kode untuk memahami apakah perubahan yang Anda lakukan akan membuat perbedaan.
Selama pengembangan dengan build debuggable
aplikasi, Anda mungkin memperhatikan ada performa yang kurang baik dan mungkin ingin mulai mengatasi masalah ini. Namun, performa aplikasi debuggable
tidak mewakili apa yang akan dilihat pengguna Anda, sehingga penting untuk memastikan dengan aplikasi non-debuggable
bahwa memang ada masalah. Dalam aplikasi debuggable
, semua kode harus diinterpretasikan oleh runtime.
Saat berpikir tentang performa di Compose, tidak ada aturan ketat yang harus Anda ikuti untuk mengimplementasikan fungsi tertentu. Anda tidak boleh melakukan hal berikut sebelum waktunya:
- Jangan mencari dan memperbaiki setiap parameter tidak stabil yang tersembunyi dalam kode Anda.
- Jangan menghapus animasi yang menyebabkan rekomposisi composable tersebut.
- Jangan melakukan pengoptimalan yang sulit dibaca berdasarkan firasat Anda.
Semua perubahan ini harus dilakukan dengan cara yang cermat menggunakan alat yang tersedia untuk memastikan bahwa perubahan tersebut mengatasi masalah performa.
Ketika menangani masalah performa, Anda harus mengikuti pendekatan ilmiah berikut:
- Tetapkan performa awal dengan pengukuran.
- Amati penyebab masalah.
- Modifikasi kode berdasarkan pengamatan.
- Ukur dan bandingkan dengan performa awal.
- Ulangi.
Jika Anda tidak mengikuti metode terstruktur, beberapa perubahan mungkin meningkatkan performa, tetapi perubahan lainnya mungkin menurunkannya. Pada akhirnya, Anda memperoleh hasil yang sama.
Sebaiknya tonton video berikut tentang cara meningkatkan performa aplikasi dengan Compose yang membahas proses memperbaiki masalah performa dan bahkan menunjukkan beberapa tips tentang cara meningkatkannya.
Membuat Profil Dasar Pengukuran
Sebelum menyelidiki masalah performa lebih dalam, buat Profil Dasar Pengukuran untuk aplikasi Anda. Di Android 6 (level API 23) dan yang lebih baru, aplikasi menjalankan kode yang diinterpretasikan saat runtime dan mengompilasi just-in-time (JIT) dan ahead-of-time (AOT) saat penginstalan. Kode yang diinterpretasikan dan dikompilasi JIT berjalan lebih lambat daripada AOT, tetapi membutuhkan lebih sedikit ruang pada disk dan memori. Itulah sebabnya tidak semua kode harus dikompilasi AOT.
Dengan menerapkan Profil Dasar Pengukuran, Anda dapat meningkatkan proses mulai aplikasi sebesar 30% dan mengurangi kode yang berjalan dalam mode JIT saat runtime sebanyak delapan kali lipat seperti yang ditunjukkan pada gambar berikut berdasarkan aplikasi contoh Now in Android:
Untuk informasi lebih lanjut mengenai Profil Dasar Pengukuran, lihat referensi berikut:
- Dokumentasi Profil Dasar Pengukuran
- Codelab Meningkatkan performa aplikasi dengan Profil Dasar Pengukuran
Mengukur performa
Untuk mengukur performa, sebaiknya Anda menyiapkan dan menulis benchmark dengan Jetpack Macrobenchmark. Macrobenchmark adalah uji instrumentasi yang berinteraksi dengan aplikasi seperti pengguna saat memantau performa aplikasi Anda. Artinya, kode tersebut tidak mencemari kode aplikasi dengan kode pengujian sehingga memberikan informasi performa yang andal.
Dalam codelab ini, kami sudah menyiapkan codebase dan menulis benchmark agar Anda hanya fokus pada perbaikan masalah performa. Jika tidak yakin mengenai cara menyiapkan dan menggunakan Macrobenchmark dalam project Anda, lihat referensi berikut:
- Codelab Memeriksa performa aplikasi dengan Macrobenchmark
- Memeriksa Performa–MAD Skills
- Dokumentasi Menulis Macrobenchmark
Dengan Macrobenchmark, Anda dapat memilih salah satu mode kompilasi berikut:
None
: Mereset status kompilasi dan menjalankan semuanya dalam mode JIT.Partial
: Melakukan pra-kompilasi aplikasi dengan Profil Dasar Pengukuran dan/atau iterasi persiapan, dan menjalankan dalam mode JIT.Full
: Melakukan pra-kompilasi seluruh kode aplikasi, sehingga tidak ada kode yang berjalan dalam mode JIT.
Dalam codelab ini, Anda hanya akan menggunakan mode CompilationMode.Full()
untuk benchmark karena yang penting hanya perubahan yang Anda buat pada kode, bukan status kompilasi aplikasi. Pendekatan ini memungkinkan Anda mengurangi varians yang disebabkan oleh kode yang berjalan dalam mode JIT, yang harus dikurangi saat menerapkan Profil Dasar Pengukuran kustom. Perhatikan bahwa mode Full
dapat berdampak negatif pada proses mulai aplikasi, jadi jangan gunakan mode ini untuk benchmark yang mengukur proses mulai aplikasi, tetapi hanya gunakan untuk benchmark yang mengukur peningkatan performa runtime.
Jika Anda sudah selesai meningkatkan performa dan ingin memeriksa performa saat pengguna menginstal aplikasi, gunakan mode CompilationMode.Partial()
yang menggunakan Profil Dasar Pengukuran.
Di bagian berikutnya, Anda akan mempelajari cara membaca rekaman aktivitas untuk menemukan masalah performa.
4. Menganalisis performa dengan pelacakan sistem
Dengan build debuggable
aplikasi, Anda dapat menggunakan Layout Inspector dengan jumlah komposisi untuk memahami dengan cepat ketika rekomposisi terjadi terlalu sering.
Namun, hal ini hanya bagian dari penyelidikan performa secara keseluruhan karena Anda hanya mendapatkan pengukuran proksi, bukan waktu sebenarnya yang diperlukan untuk merender composable tersebut. Tidak masalah jika rekomposisi terjadi N
kali ketika total durasinya kurang dari satu milidetik. Namun di sisi lain, akan jadi masalah jika komposisi hanya terjadi sekali atau dua kali dan membutuhkan waktu 100 milidetik. Sering kali, composable hanya disusun satu kali, tetapi memerlukan waktu terlalu lama dan memperlambat layar Anda.
Untuk menyelidiki masalah performa dengan andal serta mendapatkan laporan tentang apa yang dilakukan aplikasi Anda dan apakah prosesnya memerlukan waktu lebih lama dari yang seharusnya, Anda dapat menggunakan pelacakan sistem dengan pelacakan komposisi.
Pelacakan sistem memberi Anda informasi waktu tentang apa pun yang terjadi di aplikasi Anda. Hal ini tidak menambah overhead apa pun pada aplikasi. Oleh karena itu, Anda dapat menyimpannya di aplikasi produksi tanpa perlu mengkhawatirkan efek negatif terhadap performa.
Menyiapkan pelacakan komposisi
Compose otomatis mengisi beberapa informasi saat fase runtime seperti ketika terjadi rekomposisi atau ketika tata letak lambat mengambil data item. Namun, informasi tersebut tidak cukup untuk benar-benar mengetahui bagian yang bermasalah. Anda dapat meningkatkan jumlah informasi dengan menyiapkan pelacakan komposisi, yang memberi Anda nama setiap composable yang disusun selama pelacakan. Dengan begitu, Anda dapat menyelidiki masalah performa tanpa harus menambahkan banyak bagian trace("label")
kustom.
Untuk mengaktifkan Pelacakan komposisi, ikuti langkah-langkah berikut:
- Tambahkan dependensi
runtime-tracing
ke modul:app
Anda:
implementation("androidx.compose.runtime:runtime-tracing:1.0.0-beta01")
Pada tahap ini, Anda dapat merekam aktivitas sistem dengan profiler Android Studio yang akan menyertakan semua informasi, tetapi kita akan menggunakan Macrobenchmark untuk pengukuran performa dan perekaman aktivitas sistem.
- Tambahkan dependensi tambahan ke modul
:measure
untuk memungkinkan pelacakan komposisi dengan Macrobenchmark:
implementation("androidx.tracing:tracing-perfetto:1.0.0")
implementation("androidx.tracing:tracing-perfetto-binary:1.0.0")
- Tambahkan argumen instrumentasi
androidx.benchmark.fullTracing.enable=true
ke filebuild.gradle
modul:measure
:
defaultConfig {
// ...
testInstrumentationRunnerArguments["androidx.benchmark.fullTracing.enable"] = "true"
}
Untuk informasi selengkapnya tentang cara menyiapkan pelacakan komposisi, seperti cara menggunakannya dari terminal, baca dokumentasi ini.
Merekam performa awal dengan Macrobenchmark
Ada beberapa cara untuk mengambil file pelacakan sistem. Misalnya, Anda dapat merekam dengan profiler Android Studio, merekam di perangkat, atau mengambil rekaman aktivitas sistem dengan Macrobenchmark. Dalam codelab ini, Anda menggunakan rekaman aktivitas yang diambil dengan library Macrobenchmark.
Project ini berisi benchmark dalam modul :measure
yang dapat Anda jalankan untuk mendapatkan pengukuran performa. Benchmark dalam project ini disetel agar hanya menjalankan satu iterasi untuk menghemat waktu selama codelab ini. Di aplikasi sebenarnya, disarankan untuk menetapkan minimal 10 iterasi jika varians output tinggi.
Untuk merekam performa awal, gunakan uji AccelerateHeavyScreenBenchmark
yang men-scroll layar tugas pertama. Ikuti langkah-langkah berikut:
- Buka file
AccelerateHeavyScreenBenchmark.kt
. - Jalankan benchmark dengan tindakan gutter di samping class benchmark:
Benchmark ini men-scroll layar Task 1 dan mencatat waktu render frame dan bagian
rekaman aktivitas kustom.
Setelah benchmark selesai, Anda akan melihat hasilnya di panel output Android Studio:
AccelerateHeavyScreenBenchmark_accelerateHeavyScreenCompilationFull
ImagePlaceholderCount min 20.0, median 20.0, max 20.0
ImagePlaceholderMs min 22.9, median 22.9, max 22.9
ItemTagCount min 80.0, median 80.0, max 80.0
ItemTagMs min 3.2, median 3.2, max 3.2
PublishDate.registerReceiverCount min 1.0, median 1.0, max 1.0
PublishDate.registerReceiverMs min 1.9, median 1.9, max 1.9
frameDurationCpuMs P50 5.4, P90 9.0, P95 10.5, P99 57.5
frameOverrunMs P50 -4.2, P90 -3.5, P95 -3.2, P99 74.9
Traces: Iteration 0
Metrik penting dalam output ini adalah sebagai berikut:
frameDurationCpuMs
: Menunjukkan waktu yang diperlukan untuk merender frame. Makin pendek makin baik.frameOverrunMs
: Menunjukkan lama waktu frame melebihi batas, termasuk tugas di GPU. Angka negatif bagus karena berarti masih ada waktu.
Metrik lainnya, seperti metrik ImagePlaceholderMs
, menggunakan bagian rekaman aktivitas kustom dan output yang menjumlahkan durasi semua bagian tersebut dalam file rekaman aktivitas dan frekuensi terjadinya dengan metrik ImagePlaceholderCount
.
Semua metrik tersebut dapat membantu memahami apakah perubahan yang dilakukan pada codebase meningkatkan performa.
Membaca file rekaman aktivitas
Anda dapat membaca file rekaman aktivitas dari Android Studio atau dengan alat berbasis web Perfetto.
Meskipun profiler Android Studio bagus untuk membuka rekaman aktivitas dengan cepat dan menunjukkan proses aplikasi Anda, Perfetto menyediakan kemampuan penyelidikan yang lebih mendalam untuk semua proses yang berjalan di sistem dengan kueri SQL yang canggih dan banyak lagi. Dalam codelab ini, Anda menggunakan Perfetto untuk menganalisis rekaman aktivitas sistem.
- Buka situs Perfetto, yang akan memuat dasbor alat.
- Temukan rekaman aktivitas sistem yang direkam oleh Macrobenchmark di sistem file hosting Anda dan disimpan dalam folder
[module]/outputs/connected_android_test_additional_output/benchmarkRelease/connected/[device]/
. Setiap iterasi benchmark merekam file rekaman aktivitas terpisah yang masing-masing berisi interaksi yang sama dengan aplikasi Anda.
- Tarik file
AccelerateHeavyScreenBenchmark_...iter000...perfetto-trace
ke UI Perfetto dan tunggu hingga file rekaman aktivitas dimuat. - Opsional: Jika Anda tidak dapat menjalankan benchmark dan menghasilkan file rekaman aktivitas, download file rekaman aktivitas kami dan tarik ke Perfetto:
- Temukan proses aplikasi Anda yang bernama com.compose.performance. Biasanya, aplikasi latar depan ada di bawah baris informasi hardware dan beberapa baris sistem.
- Buka menu drop-down dengan nama proses aplikasi. Anda akan melihat daftar thread yang berjalan di aplikasi Anda. Biarkan file rekaman aktivitas tetap terbuka karena Anda memerlukannya di langkah berikutnya.
Untuk menemukan masalah performa di aplikasi, Anda dapat memanfaatkan linimasa Expected dan Actual di bagian atas daftar thread aplikasi Anda:
Expected Timeline memberi tahu Anda saat sistem mengharapkan frame yang dihasilkan oleh aplikasi Anda menampilkan UI yang intuitif dan berperforma baik, yang dalam hal ini 16 md dan 600 µs (1000 md/60). Actual Timeline menunjukkan durasi sebenarnya dari frame yang dihasilkan oleh aplikasi Anda, termasuk tugas GPU.
Anda mungkin akan melihat warna yang berbeda, yang menunjukkan hal berikut:
- Frame hijau: Frame dihasilkan tepat waktu.
- Frame merah: Frame mengalami jank lebih lama dari yang diharapkan. Anda harus menyelidiki tugas yang dilakukan di frame ini untuk mencegah masalah performa.
- Frame hijau muda: Frame dihasilkan dalam batas waktu, tetapi terlambat ditampilkan sehingga mengakibatkan peningkatan latensi input.
- Frame kuning: Frame mengalami jank, tetapi penyebabnya bukan aplikasi.
Ketika UI dirender di layar, perubahan harus dilakukan lebih cepat daripada durasi pembuatan frame yang diharapkan perangkat. Secara historis, durasinya adalah sekitar 16,6 md mengingat kecepatan refresh layar adalah 60 Hz, tetapi untuk perangkat Android modern mungkin sekitar 11 md atau kurang karena kecepatan refresh layar adalah 90 Hz atau lebih cepat. Durasi tersebut juga dapat berbeda untuk setiap frame karena kecepatan refresh bervariasi.
Misalnya, jika UI Anda terdiri dari 16 item, durasi pembuatan setiap item adalah sekitar 1 md agar frame tidak dilewati. Sementara itu, jika hanya ada satu item, seperti pemutar video, perlu waktu hingga 16 md untuk menyusunnya tanpa jank.
Memahami call chart pelacakan sistem
Gambar berikut adalah contoh versi sederhana rekaman aktivitas sistem yang menunjukkan rekomposisi.
Setiap batang dari atas ke bawah adalah total waktu batang di bawahnya. Batang tersebut juga sesuai dengan bagian kode fungsi yang dipanggil. Compose memanggil rekomposisi di hierarki komposisi Anda. Composable pertama adalah MaterialTheme
. MaterialTheme
berisi komposisi lokal yang menyediakan informasi penerapan tema. Dari sana, composable HomeScreen
dipanggil. Composable layar utama memanggil composable MyImage
and MyButton
sebagai bagian dari komposisinya.
Selisih dalam rekaman aktivitas sistem berasal dari kode tidak terlacak yang dijalankan karena rekaman aktivitas sistem hanya menampilkan kode yang ditandai untuk pelacakan. Kode yang dijalankan terjadi setelah MyImage
dipanggil, tetapi sebelum MyButton
dipanggil dan menghabiskan waktu yang diperlukan untuk mengukur selisih tersebut.
Pada langkah berikutnya, Anda akan menganalisis rekaman aktivitas yang diambil di langkah sebelumnya.
5. Mengakselerasi composable berat
Sebagai tugas pertama saat mencoba mengoptimalkan performa aplikasi, Anda harus mencari composable berat atau tugas yang berjalan lama di thread utama. Tugas yang berjalan lama mungkin memiliki arti yang berbeda tergantung seberapa rumit UI Anda dan berapa banyak waktu yang tersedia untuk menyusun UI.
Jadi, jika frame mengalami penurunan, Anda perlu menemukan composable yang memakan waktu terlalu lama dan mempercepatnya dengan mengurangi beban thread utama atau melewati beberapa tugas yang dilakukan di thread utama.
Untuk menganalisis rekaman aktivitas yang diambil dari pengujian AccelerateHeavyScreenBenchmark
, ikuti langkah-langkah berikut:
- Buka rekaman aktivitas sistem yang diambil di langkah sebelumnya.
- Perbesar frame panjang pertama yang berisi inisialisasi UI setelah data dimuat. Isi frame terlihat sama seperti gambar berikut:
Di rekaman aktivitas, Anda dapat melihat bahwa ada banyak hal yang terjadi dalam satu frame, yang dapat ditemukan di bagian Choreographer#doFrame
. Dari gambar, terlihat bahwa bagian tugas terbesar berasal dari composable yang berisi bagian ImagePlaceholder
yang memuat gambar berukuran besar.
Jangan memuat gambar ukuran besar di thread utama
Mungkin memuat gambar secara asinkron dari jaringan menggunakan salah satu library praktis seperti Coil atau Glidedapat dilakukan, tetapi bagaimana jika Anda ingin menampilkan gambar ukuran besar yang ada di aplikasi lokal?
Fungsi composable painterResource
umum yang memuat gambar dari resource akan memuat gambar di thread utama selama komposisi. Artinya, ukuran gambar yang besar dapat memblokir thread utama dengan beberapa tugas.
Dalam kasus Anda, Anda dapat melihat masalah sebagai bagian dari placeholder gambar asinkron. Composable painterResource
memuat gambar placeholder yang memerlukan waktu sekitar 23 md untuk dimuat.
Berikut beberapa cara untuk memperbaiki masalah ini:
- Muat gambar secara asinkron.
- Perkecil ukuran gambar agar dapat dimuat lebih cepat.
- Gunakan vektor drawable yang diskalakan berdasarkan ukuran yang diperlukan.
Untuk memperbaiki masalah performa ini, ikuti langkah-langkah berikut:
- Buka file
AccelerateHeavyScreen.kt
. - Temukan composable
imagePlaceholder()
yang memuat gambar. Gambar placeholder memiliki dimensi 1600x1600 px, yang jelas terlalu besar untuk ditampilkan.
- Ubah drawable menjadi
R.drawable.placeholder_vector
:
@Composable
fun imagePlaceholder() =
trace("ImagePlaceholder") { painterResource(R.drawable.placeholder_vector) }
- Jalankan kembali uji
AccelerateHeavyScreenBenchmark
, yang membangun kembali aplikasi dan merekam aktivitas sistem lagi. - Tarik rekaman aktivitas sistem ke dasbor Perfetto.
Selain itu, Anda dapat mendownload rekaman aktivitas:
- Cari bagian rekaman aktivitas
ImagePlaceholder
yang menunjukkan secara langsung bagian yang ditingkatkan.
- Perhatikan bahwa fungsi
ImagePlaceholder
tidak lagi memblokir thread utama.
Sebagai solusi alternatif dalam aplikasi sebenarnya, mungkin bukan gambar placeholder yang menyebabkan masalah, tetapi karya seni. Dalam hal ini, Anda dapat menggunakan composable rememberAsyncImage
Coil yang memuat composable secara asinkron. Solusi ini akan menampilkan ruang kosong hingga placeholder dimuat, jadi ketahui bahwa Anda mungkin perlu memiliki placeholder untuk jenis gambar ini.
Masih ada beberapa hal lain dengan performa kurang baik yang akan Anda atasi di langkah berikutnya.
6. Mengurangi beban operasi berat ke thread latar belakang
Jika terus menyelidiki item yang sama untuk menemukan masalah lain, Anda akan menemukan bagian dengan nama binder transaction
, yang masing-masing memerlukan waktu sekitar 1 md.
Bagian yang disebut binder transaction
menunjukkan bahwa ada komunikasi antar-proses (IPC) yang terjadi antara proses Anda dan beberapa proses sistem. Hal ini adalah cara normal untuk mengambil beberapa informasi dari sistem, seperti mengambil layanan sistem.
Transaksi ini disertakan dalam banyak API yang berkomunikasi dengan sistem. Misalnya, ketika mengambil layanan sistem dengan getSystemService
, mendaftarkan penerima siaran, atau meminta ConnectivityManager
.
Sayangnya, transaksi ini tidak memberikan banyak informasi tentang apa yang diminta, jadi Anda harus menganalisis kode pada penggunaan API yang disebutkan dan kemudian menambahkan bagian trace
kustom untuk memastikan bahwa bagian tersebut memang bermasalah.
Untuk meningkatkan transaksi binder, ikuti langkah-langkah berikut:
- Buka file
AccelerateHeavyScreen.kt
. - Temukan composable
PublishedText
. Composable ini menggunakan format tanggal dan waktu sesuai zona waktu saat ini dan mendaftarkan objekBroadcastReceiver
yang melacak perubahan zona waktu. Composable ini berisi variabel statuscurrentTimeZone
dengan zona waktu sistem default sebagai nilai awal, laluDisposableEffect
yang mendaftarkan penerima siaran untuk perubahan zona waktu. Terakhir, composable ini menampilkan tanggal dan waktu yang diformat denganText
.DisposableEffect
, yang merupakan pilihan bagus dalam skenario ini karena Anda memerlukan cara untuk membatalkan pendaftaran penerima siaran, yang dilakukan di lambdaonDispose
. Masalahnya, kode di dalamDisposableEffect
memblokir thread utama:
@Composable
fun PublishedText(published: Instant, modifier: Modifier = Modifier) {
val context = LocalContext.current
var currentTimeZone: TimeZone by remember { mutableStateOf(TimeZone.currentSystemDefault()) }
DisposableEffect(Unit) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
currentTimeZone = TimeZone.currentSystemDefault()
}
}
// TODO Codelab task: Wrap with a custom trace section
context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
onDispose { context.unregisterReceiver(receiver) }
}
Text(
text = published.format(currentTimeZone),
style = MaterialTheme.typography.labelMedium,
modifier = modifier
)
}
- Gabungkan
context.registerReceiver
dengan panggilantrace
untuk memastikan bahwa memang inilah penyebab semuabinder transactions
:
trace("PublishDate.registerReceiver") {
context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
}
Secara umum, kode yang berjalan selama itu di thread utama mungkin tidak menimbulkan banyak masalah, tetapi fakta bahwa transaksi ini berjalan untuk setiap item yang terlihat di layar mungkin menimbulkan masalah. Dengan asumsi ada enam item yang terlihat di layar, keenam item tersebut harus disusun dengan frame pertama. Panggilan ini saja dapat memakan waktu 12 md, yang hampir merupakan batas waktu keseluruhan untuk satu frame.
Untuk memperbaikinya, Anda perlu mengurangi beban pendaftaran siaran ke thread lain. Anda dapat melakukannya dengan coroutine.
- Dapatkan cakupan yang terikat dengan siklus proses composable
val scope = rememberCoroutineScope()
. - Di dalam efek, luncurkan coroutine pada operator yang bukan
Dispatchers.Main
. Misalnya,Dispatchers.IO
dalam hal ini. Dengan cara ini, pendaftaran siaran tidak memblokir thread utama, tetapicurrentTimeZone
status sebenarnya disimpan di thread utama.
val scope = rememberCoroutineScope()
DisposableEffect(Unit) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
currentTimeZone = TimeZone.currentSystemDefault()
}
}
// launch the coroutine on Dispatchers.IO
scope.launch(Dispatchers.IO) {
trace("PublishDate.registerReceiver") {
context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
}
}
onDispose { context.unregisterReceiver(receiver) }
}
Ada satu langkah lagi untuk mengoptimalkan hal ini. Anda hanya memerlukan satu penerima siaran untuk semua item dalam daftar. Anda harus mengangkatnya!
Anda dapat mengangkatnya dan meneruskan parameter zona waktu ke bawah hierarki composable atau menggunakan komposisi lokal karena parameter tersebut tidak digunakan di banyak tempat pada UI.
Untuk tujuan codelab ini, Anda menyimpan penerima siaran sebagai bagian dari hierarki composable. Namun, di aplikasi sebenarnya, memisahkannya dalam lapisan data mungkin bermanfaat untuk mencegah pencemaran kode UI Anda.
- Tentukan komposisi lokal dengan zona waktu sistem default:
val LocalTimeZone = compositionLocalOf { TimeZone.currentSystemDefault() }
- Perbarui composable
ProvideCurrentTimeZone
yang menggunakan lambdacontent
untuk menyediakan zona waktu saat ini:
@Composable
fun ProvideCurrentTimeZone(content: @Composable () -> Unit) {
var currentTimeZone = TODO()
CompositionLocalProvider(
value = LocalTimeZone provides currentTimeZone,
content = content,
)
}
- Pindahkan
DisposableEffect
dari composablePublishedText
ke composable baru untuk mengangkatnya dan ganticurrentTimeZone
dengan status dan efek samping:
@Composable
fun ProvideCurrentTimeZone(content: @Composable () -> Unit) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
var currentTimeZone: TimeZone by remember { mutableStateOf(TimeZone.currentSystemDefault()) }
DisposableEffect(Unit) {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
currentTimeZone = TimeZone.currentSystemDefault()
}
}
scope.launch(Dispatchers.IO) {
trace("PublishDate.registerReceiver") {
context.registerReceiver(receiver, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
}
}
onDispose { context.unregisterReceiver(receiver) }
}
CompositionLocalProvider(
value = LocalTimeZone provides currentTimeZone,
content = content,
)
}
- Gabungkan composable dengan
ProvideCurrentTimeZone
agar komposisi lokalnya valid. Anda dapat menggabungkan seluruhAccelerateHeavyScreen
seperti yang ditampilkan dalam cuplikan berikut:
@Composable
fun AccelerateHeavyScreen(items: List<HeavyItem>, modifier: Modifier = Modifier) {
// TODO: Codelab task: Wrap this with timezone provider
ProvideCurrentTimeZone {
Box(
modifier = modifier
.fillMaxSize()
.padding(24.dp)
) {
ScreenContent(items = items)
if (items.isEmpty()) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
}
- Ubah composable
PublishedText
agar hanya memuat fungsi pemformatan dasar dan membaca nilai komposisi lokal saat ini melaluiLocalTimeZone.current
:
@Composable
fun PublishedText(published: Instant, modifier: Modifier = Modifier) {
Text(
text = published.format(LocalTimeZone.current),
style = MaterialTheme.typography.labelMedium,
modifier = modifier
)
}
- Jalankan kembali benchmark untuk membangun aplikasi.
Selain itu, Anda dapat mendownload rekaman aktivitas sistem dengan kode yang benar:
- Tarik file rekaman aktivitas ke dasbor Perfetto. Semua bagian
binder transactions
hilang dari thread utama. - Cari nama bagian yang sama dengan langkah sebelumnya. Anda dapat menemukannya di salah satu thread lain yang dibuat oleh coroutine (
DefaultDispatch
):
7. Menghapus subkomposisi yang tidak perlu
Anda telah memindahkan kode berat dari thread utama sehingga tidak lagi memblokir komposisi. Masih ada potensi peningkatan. Anda dapat menghapus beberapa overhead tidak perlu dalam bentuk composable LazyRow
di setiap item.
Dalam contoh, setiap item berisi baris tag seperti yang ditandai dalam gambar berikut:
Baris ini diimplementasikan dengan composable LazyRow
karena menulis dengan cara ini lebih mudah. Teruskan item ke composable LazyRow
yang akan menangani sisanya:
@Composable
fun ItemTags(tags: List<String>, modifier: Modifier = Modifier) {
// TODO: remove unnecessary lazy layout
LazyRow(
modifier = modifier
.padding(4.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
items(tags) { ItemTag(it) }
}
}
Masalahnya adalah, meskipun tata letak Lazy
unggul dalam tata letak tempat Anda memiliki lebih banyak item daripada ukuran yang dibatasi, muncul sejumlah biaya tambahan yang tidak perlu jika komposisi lambat tidak diperlukan.
Mengingat sifat composable Lazy
, yang menggunakan composable SubcomposeLayout
, composable tersebut selalu ditampilkan sebagai beberapa bagian tugas. Tugas pertama adalah container dan tugas kedua adalah item yang saat ini terlihat di layar. Anda juga dapat menemukan rekaman aktivitas compose:lazylist:prefetch
dalam rekaman aktivitas sistem, yang menunjukkan bahwa item tambahan masuk ke area tampilan. Oleh karena itu, item tersebut diambil datanya agar siap lebih awal.
Untuk menentukan durasi waktu yang diperlukan dalam kasus Anda, buka file rekaman aktivitas yang sama. Anda dapat melihat bahwa ada bagian yang terlepas dari item induk. Setiap item terdiri dari item sebenarnya yang sedang disusun dan kemudian item tag. Dengan cara ini, setiap item menghasilkan waktu komposisi sekitar 2,5 milidetik, yang jika dikalikan dengan jumlah item yang terlihat, akan menghasilkan banyak bagian tugas.
Untuk memperbaiki hal ini, ikuti langkah-langkah berikut:
- Buka file
AccelerateHeavyScreen.kt
dan temukan composableItemTags
. - Ubah implementasi
LazyRow
menjadi composableRow
yang melakukan iterasi pada daftartags
seperti dalam cuplikan berikut:
@Composable
fun ItemTags(tags: List<String>, modifier: Modifier = Modifier) {
Row(
modifier = modifier
.padding(4.dp)
.fillMaxWidth()
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
tags.forEach { ItemTag(it) }
}
}
- Jalankan kembali benchmark yang juga akan membangun aplikasi.
- Opsional: Download pelacakan sistem dengan kode yang benar:
- Temukan bagian
ItemTag
, perhatikan bahwa waktu yang dibutuhkan lebih sedikit, dan menggunakan bagian rootCompose:recompose
yang sama.
Situasi serupa mungkin terjadi pada container lain yang menggunakan composable SubcomposeLayout
, misalnya composable BoxWithConstraints
. Hal ini dapat mencakup pembuatan item di seluruh bagian Compose:recompose
, yang mungkin tidak ditampilkan secara langsung sebagai frame yang mengalami jank, tetapi dapat terlihat oleh pengguna. Jika memungkinkan, cobalah untuk menghindari composable BoxWithConstraints
di setiap item karena mungkin hanya diperlukan saat Anda membuat UI yang berbeda berdasarkan ruang yang tersedia.
Di bagian ini, Anda mempelajari cara memperbaiki komposisi yang memakan waktu terlalu lama.
8. Membandingkan hasil dengan benchmark awal
Sekarang setelah selesai mengoptimalkan performa layar, Anda harus membandingkan hasil benchmark dengan hasil awal.
- Buka Test History di panel run Android Studio
- Pilih proses terlama yang terkait dengan benchmark awal tanpa perubahan apa pun dan bandingkan metrik
frameDurationCpuMs
danframeOverrunMs
. Anda akan melihat hasil yang serupa dengan tabel berikut:
Sebelum
AccelerateHeavyScreenBenchmark_accelerateHeavyScreenCompilationFull
ImagePlaceholderCount min 20.0, median 20.0, max 20.0
ImagePlaceholderMs min 22.9, median 22.9, max 22.9
ItemTagCount min 80.0, median 80.0, max 80.0
ItemTagMs min 3.2, median 3.2, max 3.2
PublishDate.registerReceiverCount min 1.0, median 1.0, max 1.0
PublishDate.registerReceiverMs min 1.9, median 1.9, max 1.9
frameDurationCpuMs P50 5.4, P90 9.0, P95 10.5, P99 57.5
frameOverrunMs P50 -4.2, P90 -3.5, P95 -3.2, P99 74.9
Traces: Iteration 0
- Pilih run terbaru yang terkait dengan benchmark serta semua pengoptimalannya. Anda akan melihat hasil yang serupa dengan tabel berikut:
Setelah
AccelerateHeavyScreenBenchmark_accelerateHeavyScreenCompilationFull
ImagePlaceholderCount min 20.0, median 20.0, max 20.0
ImagePlaceholderMs min 2.9, median 2.9, max 2.9
ItemTagCount min 80.0, median 80.0, max 80.0
ItemTagMs min 3.4, median 3.4, max 3.4
PublishDate.registerReceiverCount min 1.0, median 1.0, max 1.0
PublishDate.registerReceiverMs min 1.1, median 1.1, max 1.1
frameDurationCpuMs P50 4.3, P90 7.7, P95 8.8, P99 33.1
frameOverrunMs P50 -11.4, P90 -8.3, P95 -7.3, P99 41.8
Traces: Iteration 0
Jika memeriksa baris frameOverrunMs
secara khusus, Anda dapat melihat bahwa semua persentilnya meningkat:
P50 | P90 | P95 | P99 | |
sebelum | -4,2 | -3,5 | -3,2 | 74,9 |
setelah | -11,4 | -8,3 | -7,3 | 41,8 |
peningkatan | 171% | 137% | 128% | 44% |
Di bagian berikutnya, Anda akan mempelajari cara memperbaiki komposisi yang terlalu sering terjadi.
9. Mencegah rekomposisi yang tidak perlu
Compose memiliki 3 tahap:
- Komposisi menentukan apa yang akan ditampilkan dengan membuat hierarki composable.
- Tata Letak mengambil hierarki tersebut dan menentukan tempat composable akan muncul di layar.
- Gambar menggambar composable di layar.
Urutan tahap ini umumnya sama, memungkinkan data mengalir dalam satu arah dari komposisi ke tata letak hingga gambar untuk menghasilkan frame UI.
BoxWithConstraints
, tata letak lambat (misalnya LazyColumn
atau LazyVerticalGrid
), dan semua tata letak berdasarkan composable SubcomposeLayout
adalah pengecualian penting, karena komposisi turunannya bergantung pada tahap tata letak induknya.
Secara umum, komposisi adalah tahap yang paling mahal untuk dijalankan karena ada banyak tugas yang harus dilakukan dan Anda juga dapat menyebabkan rekomposisi pada composable lain yang tidak terkait.
Sebagian besar frame berisi ketiga tahap tersebut, tetapi sebenarnya Compose dapat melewati seluruh tahap jika tidak ada tugas yang harus dilakukan. Anda dapat memanfaatkan kemampuan ini untuk meningkatkan performa aplikasi Anda.
Menunda tahap komposisi dengan pengubah lambda
Fungsi composable dijalankan dalam tahap komposisi. Untuk mengizinkan kode dijalankan pada waktu berbeda, Anda dapat menyediakannya sebagai fungsi lambda.
Untuk melakukannya, ikuti langkah berikut:
- Buka file
PhasesComposeLogo.kt
. - Buka layar Task 2 dalam aplikasi. Anda akan melihat logo yang memantul dari tepi layar.
- Buka Layout Inspector dan periksa Recomposition counts. Anda akan melihat jumlah rekomposisi yang meningkat pesat.
- Opsional: Temukan file
PhasesComposeLogoBenchmark.kt
dan jalankan untuk mengambil rekaman aktivitas sistem guna melihat komposisi bagian rekaman aktivitasPhasesComposeLogo
yang terjadi di setiap frame. Rekomposisi ditampilkan dalam rekaman aktivitas sebagai bagian berulang dengan nama yang sama.
- Jika perlu, tutup profiler dan Layout Inspector, lalu kembali ke kode. Anda akan melihat composable
PhaseComposeLogo
yang terlihat seperti berikut:
@Composable
fun PhasesComposeLogo() = trace("PhasesComposeLogo") {
val logo = painterResource(id = R.drawable.compose_logo)
var size by remember { mutableStateOf(IntSize.Zero) }
val logoPosition by logoPosition(size = size, logoSize = logo.intrinsicSize)
Box(
modifier = Modifier
.fillMaxSize()
.onPlaced {
size = it.size
}
) {
with(LocalDensity.current) {
Image(
painter = logo,
contentDescription = "logo",
modifier = Modifier.offset(logoPosition.x.toDp(), logoPosition.y.toDp())
)
}
}
}
Composable logoPosition
berisi logika yang mengubah statusnya pada setiap frame dan terlihat sebagai berikut:
@Composable
fun logoPosition(size: IntSize, logoSize: Size): State<IntOffset> =
produceState(initialValue = IntOffset.Zero, size, logoSize) {
if (size == IntSize.Zero) {
this.value = IntOffset.Zero
return@produceState
}
var xDirection = 1
var yDirection = 1
while (true) {
withFrameMillis {
value += IntOffset(x = MOVE_SPEED * xDirection, y = MOVE_SPEED * yDirection)
if (value.x <= 0 || value.x >= size.width - logoSize.width) {
xDirection *= -1
}
if (value.y <= 0 || value.y >= size.height - logoSize.height) {
yDirection *= -1
}
}
}
}
Status sedang dibaca dalam composable PhasesComposeLogo
dengan pengubah Modifier.offset(x.dp, y.dp)
, yang berarti status dibaca dalam komposisi.
Pengubah ini adalah penyebab aplikasi merekomposisi di setiap frame animasi ini. Dalam hal ini, ada alternatif sederhana: pengubah Offset
berbasis lambda.
- Perbarui composable
Image
untuk menggunakan pengubahModifier.offset
, yang menerima lambda yang menampilkan objekIntOffset
seperti dalam cuplikan berikut:
Image(
painter = logo,
contentDescription = "logo",
modifier = Modifier.offset { IntOffset(logoPosition.x, logoPosition.y) }
)
- Jalankan kembali aplikasi dan periksa Layout Inspector. Anda akan melihat bahwa animasi tidak lagi menghasilkan rekomposisi apa pun.
Ingat, Anda tidak perlu melakukan rekomposisi hanya untuk menyesuaikan tata letak layar, terutama saat scrolling, yang menyebabkan frame mengalami jank. Rekomposisi yang terjadi selama scrolling hampir selalu tidak diperlukan dan harus dihindari.
Pengubah lambda lainnya
Pengubah Modifier.offset
bukan satu-satunya pengubah dengan versi lambda. Dalam tabel berikut, Anda dapat melihat pengubah umum yang akan melakukan rekomposisi setiap saat, yang dapat diganti dengan alternatif yang ditangguhkan ketika meneruskan nilai status yang sering berubah:
Pengubah umum | Alternatif yang ditangguhkan |
|
|
|
|
|
|
10. Menunda tahap Compose dengan tata letak kustom
Menggunakan pengubah berbasis lambda biasanya adalah cara termudah untuk menghindari pembatalan validasi komposisi, tetapi terkadang tidak ada satu pun pengubah berbasis lambda yang dapat melakukan apa yang Anda perlukan. Dalam kasus ini, Anda dapat langsung mengimplementasikan tata letak kustom atau bahkan composable Canvas
dan langsung menuju tahap gambar. Pembacaan status Compose yang dilakukan di dalam tata letak kustom hanya akan membuat tata letak tidak valid dan melewati rekomposisi. Sebagai panduan umum, jika hanya ingin menyesuaikan tata letak atau ukuran, tetapi tidak menambahkan atau menghapus composable, Anda dapat memperoleh efek tanpa membatalkan validasi komposisi sama sekali.
Untuk melakukannya, ikuti langkah berikut:
- Buka file
PhasesAnimatedShape.kt
, lalu jalankan aplikasi. - Buka layar Task 3. Layar ini berisi bentuk yang berubah ukurannya saat Anda mengklik tombol. Nilai ukuran dianimasikan dengan Animation API Compose
animateDpAsState
.
- Buka Layout Inspector.
- Klik Toggle size.
- Perhatikan bahwa bentuk direkomposisi di setiap frame animasi.
Composable MyShape
menggunakan objek size
sebagai parameter, yang merupakan pembacaan status. Artinya, ketika objek size
berubah, composable PhasesAnimatedShape
(cakupan rekomposisi terdekat) akan direkomposisi. Selanjutnya, composable MyShape
direkomposisi karena inputnya telah berubah.
Untuk melewati rekomposisi, ikuti langkah-langkah berikut:
- Ubah parameter
size
menjadi fungsi lambda sehingga perubahan ukuran tidak secara langsung merekomposisi composableMyShape
:
@Composable
fun MyShape(
size: () -> Dp,
modifier: Modifier = Modifier
) {
// ...
- Perbarui situs panggilan di composable
PhasesAnimatedShape
untuk menggunakan fungsi lambda:
MyShape(size = { size }, modifier = Modifier.align(Alignment.Center))
Mengubah parameter size
menjadi lambda akan menunda pembacaan status. Sekarang hal itu terjadi saat lambda dipanggil.
- Ubah isi composable
MyShape
menjadi berikut:
Box(
modifier = modifier
.background(color = Purple80, shape = CircleShape)
.layout { measurable, _ ->
val sizePx = size()
.roundToPx()
.coerceAtLeast(0)
val constraints = Constraints.fixed(
width = sizePx,
height = sizePx,
)
val placeable = measurable.measure(constraints)
layout(sizePx, sizePx) {
placeable.place(0, 0)
}
}
)
Pada baris pertama pengubah layout
mengukur lambda, Anda dapat melihat bahwa lambda size
dipanggil. Karena ada di dalam pengubah layout
, hal ini hanya membuat tata letak tidak valid, bukan komposisi.
- Jalankan kembali aplikasi, buka layar Task 3, lalu buka Layout Inspector.
- Klik Toggle Size lalu amati bahwa ukuran bentuk yang dianimasikan sama seperti sebelumnya, tetapi composable
MyShape
tidak mengalami rekomposisi.
11. Mencegah rekomposisi dengan class stabil
Compose menghasilkan kode yang dapat melewati eksekusi composable jika semua parameter inputnya stabil dan tidak berubah dari komposisi sebelumnya. Suatu jenis dikatakan stabil jika tidak dapat diubah atau jika mesin Compose dapat mengetahui apakah nilainya telah berubah saat rekomposisi.
Jika tidak yakin apakah suatu composable stabil, mesin Compose akan menganggapnya tidak stabil dan tidak akan menghasilkan logika kode untuk melewati rekomposisi, yang berarti bahwa composable akan direkomposisi setiap saat. Hal ini dapat terjadi ketika class bukan jenis primitif dan salah satu situasi berikut terjadi:
- Class dapat berubah Misalnya, class berisi properti yang dapat berubah.
- Class ditentukan dalam modul Gradle yang tidak menggunakan Compose. Class tidak memiliki dependensi pada compiler Compose.
- Class berisi properti tidak stabil.
Perilaku ini mungkin tidak diinginkan dalam beberapa kasus, sehingga menyebabkan masalah performa dan dapat diubah ketika Anda melakukan hal berikut:
- Mengaktifkan mode skipping kuat.
- Menganotasi parameter dengan anotasi
@Immutable
atau@Stable
. - Menambahkan class ke file konfigurasi stabilitas.
Untuk informasi selengkapnya mengenai stabilitas, baca dokumentasi ini.
Dalam tugas ini, Anda memiliki daftar item yang dapat ditambahkan, dihapus, atau diperiksa, dan Anda perlu memastikan bahwa item tidak direkomposisi jika tidak diperlukan. Ada dua jenis item, yaitu item yang dibuat ulang setiap saat dan yang tidak.
Item yang dibuat ulang setiap saat bertindak sebagai simulasi kasus penggunaan nyata ketika data berasal dari database lokal (misalnya Room atau sqlDelight) atau sumber data jarak jauh (seperti permintaan API atau entitas Firestore), dan menampilkan instance baru dari objek setiap kali ada perubahan.
Beberapa composable dilengkapi pengubah Modifier.recomposeHighlighter()
, yang dapat Anda temukan di repositori GitHub kami. Pengubah ini menampilkan batas setiap kali composable direkomposisi dan dapat berfungsi sebagai solusi sementara alternatif untuk Layout Inspector.
Mengaktifkan mode skipping kuat
Compiler Jetpack Compose 1.5.4 dan yang lebih baru dilengkapi dengan opsi untuk mengaktifkan mode skipping kuat, yang berarti bahwa composable dengan parameter tidak stabil dapat menghasilkan kode skipping. Mode ini diharapkan dapat secara drastis mengurangi jumlah composable yang tidak dapat dilewati dalam proyek Anda, sehingga meningkatkan performa tanpa perubahan kode apa pun.
Untuk parameter yang tidak stabil, logika skipping dibandingkan dengan persamaan instance, yang berarti parameter tersebut akan dilewati jika instance yang sama diteruskan ke composable seperti pada kasus sebelumnya. Sebaliknya, parameter stabil menggunakan persamaan struktural (dengan memanggil metode Object.equals()
) untuk menentukan logika skipping.
Selain logika skipping, mode skipping kuat juga otomatis mengingat lambda yang ada di dalam fungsi composable. Artinya, Anda tidak memerlukan panggilan remember
untuk menggabungkan fungsi lambda, misalnya fungsi yang memanggil metode ViewModel
.
Mode skipping kuat dapat diaktifkan berdasarkan modul Gradle.
Untuk mengaktifkannya, ikuti langkah-langkah berikut:
- Buka file
build.gradle.kts
aplikasi. - Perbarui blok
composeCompiler
dengan cuplikan berikut:
composeCompiler {
// Not required in Kotlin 2.0 final release
suppressKotlinVersionCompatibilityCheck = "2.0.0-RC1"
// This settings enables strong-skipping mode for all module in this project.
// As an effect, Compose can skip a composable even if it's unstable by comparing it's instance equality (===).
enableExperimentalStrongSkippingMode = true
}
Tindakan ini menambahkan argumen compiler experimentalStrongSkipping
ke modul Gradle.
- Klik Sync project with Gradle files.
- Bangun ulang project.
- Buka layar Task 5, lalu perhatikan bahwa item yang menggunakan persamaan struktural ditandai dengan ikon
EQU
dan tidak direkomposisi saat Anda berinteraksi dengan daftar item.
Namun, jenis item lainnya masih direkomposisi. Anda akan memperbaikinya di langkah berikutnya.
Memperbaiki stabilitas dengan anotasi
Seperti disebutkan sebelumnya, jika mode skipping kuat diaktifkan, composable akan melewati eksekusinya jika parameter memiliki instance yang sama seperti komposisi sebelumnya. Namun, hal ini tidak berlaku pada situasi ketika setiap perubahan akan menghasilkan instance baru dari class yang tidak stabil.
Dalam situasi Anda, class StabilityItem
tidak stabil karena berisi properti LocalDateTime
yang tidak stabil.
Untuk memperbaiki stabilitas class ini, ikuti langkah-langkah berikut:
- Buka file
StabilityViewModel.kt
. - Temukan class
StabilityItem
dan beri anotasi dengan anotasi@Immutable
:
// TODO Codelab task: make this class Stable
@Immutable
data class StabilityItem(
val id: Int,
val type: StabilityItemType,
val name: String,
val checked: Boolean,
val created: LocalDateTime
)
- Bangun kembali aplikasi.
- Buka layar Task 5 layar dan perhatikan bahwa tidak ada item daftar yang direkomposisi.
Class ini sekarang menggunakan persamaan struktural untuk memeriksa apakah komposisinya berubah dari sebelumnya dan tidak melakukan rekomposisi.
Masih ada composable yang merujuk pada tanggal perubahan terakhir, yang terus melakukan rekomposisi terlepas dari apa yang Anda lakukan hingga saat ini.
Memperbaiki stabilitas dengan file konfigurasi
Pendekatan sebelumnya berfungsi dengan baik untuk class yang merupakan bagian dari codebase Anda. Namun, class yang berada di luar jangkauan Anda, seperti class dari library pihak ketiga atau class library standar, tidak dapat diedit.
Anda dapat mengaktifkan file konfigurasi stabilitas yang menggunakan class (dengan kemungkinan karakter pengganti) yang akan dianggap stabil.
Untuk mengaktifkannya, ikuti langkah-langkah berikut:
- Buka file
build.gradle.kts
aplikasi. - Tambahkan opsi
stabilityConfigurationFile
ke blokcomposeCompiler
:
composeCompiler {
...
stabilityConfigurationFile = project.rootDir.resolve("stability_config.conf")
}
- Sinkronkan project dengan file Gradle.
- Buka file
stability_config.conf
di folder root project ini di samping fileREADME.md
. - Tambahkan cuplikan berikut:
// TODO Codelab task: Make a java.time.LocalDate class stable.
java.time.LocalDate
- Bangun kembali aplikasi. Jika tanggalnya tetap sama, class
LocalDateTime
tidak akan menyebabkan composable Latest change was YYYY-MM-DD direkomposisi.
Di aplikasi, Anda dapat memperluas file agar berisi pola, sehingga Anda tidak perlu menulis semua class yang harus dianggap stabil. Jadi dalam kasus Anda, Anda dapat menggunakan karakter pengganti java.time.*
, yang akan memperlakukan semua class dalam paket sebagai stabil, seperti Instant
, LocalDateTime
, ZoneId
, dan class lain dari java time.
Dengan mengikuti langkah-langkah ini, tidak ada item di layar ini yang direkomposisi kecuali item yang ditambahkan atau melakukan interaksi, yang merupakan perilaku yang diharapkan.
12. Selamat
Selamat, Anda telah mengoptimalkan performa aplikasi Compose! Meskipun hanya menampilkan sebagian kecil masalah performa yang mungkin Anda temui di aplikasi, Anda telah mempelajari cara melihat potensi masalah lainnya dan cara memperbaikinya.
Apa selanjutnya?
Jika Anda belum membuat Profil Dasar Pengukuran untuk aplikasi, kami sangat menyarankan Anda melakukannya.
Anda dapat mengikuti codelab Meningkatkan performa aplikasi dengan Profil Dasar Pengukuran. Jika Anda ingin informasi lebih lanjut tentang menyiapkan benchmark, lihat codelab Memeriksa performa aplikasi dengan Macrobenchmark.