Konsep dan penerapan Jetpack Compose
Mulai dari Android 3.0 (level API 11), pipeline rendering 2D Android mendukung akselerasi hardware, yang berarti semua operasi gambar yang dilakukan di kanvas View akan menggunakan GPU.
Karena perlu resource yang lebih tinggi untuk mengaktifkan akselerasi hardware, aplikasi Anda akan mengonsumsi lebih banyak RAM.
Akselerasi hardware diaktifkan secara default jika level API Target Anda adalah >=14, tetapi juga dapat diaktifkan secara eksplisit. Jika aplikasi Anda hanya menggunakan
tampilan standar dan Drawable, mengaktifkannya secara global tidak akan menyebabkan
efek gambar yang merugikan. Namun, karena akselerasi hardware tidak didukung untuk semua operasi gambar 2D, mengaktifkannya mungkin akan memengaruhi beberapa tampilan kustom atau panggilan gambar Anda. Masalah biasanya bermanifestasi sebagai
elemen tak terlihat, pengecualian, atau piksel yang salah dirender. Untuk mengatasinya,
Android menyediakan opsi untuk mengaktifkan atau menonaktifkan akselerasi hardware di
beberapa tingkat. Lihat Mengontrol akselerasi hardware.
Jika aplikasi Anda menjalankan gambar kustom, uji aplikasi Anda pada hardware sebenarnya dengan akselerasi hardware diaktifkan untuk menemukan masalah. Bagian Dukungan untuk operasi gambar menjelaskan masalah umum terkait akselerasi hardware dan cara mengatasinya.
Lihat juga OpenGL dengan Framework API dan Renderscript.
Mengontrol akselerasi hardware
Anda dapat mengontrol akselerasi hardware di tingkat berikut:
- Aplikasi
- Aktivitas
- Jendela
- Lihat
Tingkat aplikasi
Dalam file manifes Android Anda, tambahkan atribut berikut ke tag
<application> untuk mengaktifkan akselerasi hardware di seluruh
aplikasi Anda:
<application android:hardwareAccelerated="true" ...>
Tingkat aktivitas
Jika aplikasi Anda tidak berperilaku sebagaimana mestinya saat akselerasi hardware diaktifkan secara global, Anda juga dapat mengontrolnya untuk aktivitas tertentu. Untuk mengaktifkan atau menonaktifkan akselerasi hardware di tingkat aktivitas, Anda dapat menggunakan atribut android:hardwareAccelerated untuk elemen <activity>. Contoh
berikut mengaktifkan akselerasi hardware untuk keseluruhan aplikasi, tetapi
menonaktifkannya untuk satu aktivitas:
<application android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Tingkat jendela
Jika memerlukan kontrol yang lebih terperinci, Anda dapat mengaktifkan akselerasi hardware untuk jendela tertentu dengan kode berikut:
Kotlin
window.setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED )
Java
getWindow().setFlags( WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
Tingkat tampilan
Anda dapat menonaktifkan akselerasi hardware untuk setiap tampilan pada waktu proses dengan kode berikut:
Kotlin
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
Java
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Menentukan apakah tampilan mengaktifkan akselerasi hardware
Terkadang, aplikasi perlu mengetahui apakah saat ini akselerasi hardware diaktifkan, terutama untuk hal-hal seperti tampilan kustom. Hal ini sangat berguna jika aplikasi Anda melakukan banyak operasi gambar kustom dan tidak semua operasi didukung dengan baik oleh pipeline rendering yang baru.
Ada dua cara untuk memeriksa apakah akselerasi hardware diaktifkan pada aplikasi:
View.isHardwareAcceleratedmenampilkantruejikaViewdihubungkan ke jendela yang diakselerasi dengan hardware.Canvas.isHardwareAcceleratedmenampilkantruejikaCanvasdiakselerasi dengan hardware.
Jika Anda harus menjalankan pemeriksaan ini dalam kode gambar, gunakan
Canvas.isHardwareAccelerated, bukan
View.isHardwareAccelerated, jika memungkinkan. Saat tampilan dihubungkan ke jendela dengan akselerasi hardware, tampilan tersebut masih dapat digambar menggunakan Canvas tanpa akselerasi hardware. Hal ini terjadi, misalnya, saat menggambar tampilan ke dalam
bitmap untuk keperluan caching.
Model gambar Android
Jika akselerasi hardware diaktifkan, framework Android akan menggunakan model gambar baru yang memanfaatkan daftar tampilan untuk merender aplikasi Anda ke layar. Untuk memahami daftar tampilan dan pengaruhnya terhadap aplikasi, sebaiknya pahami juga cara Android menggambar tampilan tanpa akselerasi hardware. Bagian berikut menjelaskan model gambar berbasis software dan dengan akselerasi hardware.
Model gambar berbasis software
Dalam model gambar berbasis software, tampilan digambar dalam dua langkah berikut:
- Membatalkan hierarki
- Menggambar hierarki
Setiap kali perlu mengubah bagian UI-nya, aplikasi akan memanggil
invalidate() (atau salah satu variannya) di tampilan mana saja yang kontennya telah
berubah. Pesan pembatalan akan disebarkan ke seluruh hierarki tampilan untuk menghitung area layar yang perlu digambar ulang (area kotor). Selanjutnya, sistem Android akan menggambar tampilan di hierarki tersebut yang berpotongan dengan area kotor. Sayangnya, ada dua kelemahan pada model gambar ini:
Pertama, model ini memerlukan eksekusi banyak kode di setiap operasi gambar. Misalnya, jika aplikasi Anda memanggil
invalidatedi sebuah tombol, dan tombol tersebut berada di atas tampilan lain, maka sistem Android perlu menggambar ulang tampilan meskipun tampilan itu sendiri belum berubah.Masalah kedua adalah model gambar ini dapat menyembunyikan bug dalam aplikasi Anda. Karena sistem Android menggambar ulang tampilan saat berpotongan dengan area kotor, tampilan yang kontennya sudah Anda ubah mungkin akan digambar ulang sekalipun
invalidatetidak dipanggil di tampilan itu. Jika itu terjadi, Anda mengandalkan pembatalan tampilan lain untuk mendapatkan perilaku yang tepat. Perilaku ini dapat berubah setiap kali Anda memodifikasi aplikasi. Oleh karena itu, Anda harus selalu memanggilinvalidatedi tampilan kustom setiap kali Anda memodifikasi data atau status yang memengaruhi kode gambar tampilan tersebut.
Model gambar dengan akselerasi hardware
Sistem Android tetap menggunakan invalidate dan draw untuk meminta update layar dan merender tampilan, tetapi menangani operasi gambar yang sebenarnya dengan cara berbeda.
Bukanya menjalankan perintah gambar dengan seketika, sistem Android mencatatnya di dalam daftar tampilan, yang berisi output kode gambar hierarki tampilan tersebut. Pengoptimalan lainnya adalah sistem Android hanya perlu merekam dan memperbarui daftar tampilan untuk tampilan yang ditandai sebagai area kotor oleh panggilan invalidate. Tampilan yang belum dibatalkan dapat digambar ulang
dengan menerbitkan kembali daftar tampilan yang telah direkam sebelumnya. Model gambar baru ini terdiri dari tiga tahap:
Membatalkan hierarki
Merekam dan memperbarui daftar display
Menggambar daftar tampilan
Dengan model ini, Anda tidak dapat mengandalkan tampilan yang berpotongan dengan area kotor untuk menjalankan metode draw-nya. Untuk memastikan bahwa sistem Android mencatat daftar tampilan
suatu tampilan, Anda harus memanggil invalidate. Jika Anda lupa melakukannya, tampilan akan terlihat sama meskipun telah diubah.
Penggunaan daftar tampilan juga menunjang performa animasi karena penetapan properti tertentu, seperti alfa atau rotasi, tidak memerlukan pembatalan tampilan yang ditargetkan (hal tersebut dilakukan otomatis). Pengoptimalan ini juga berlaku untuk
tampilan dengan daftar tampilan (tampilan ketika aplikasi Anda diakselerasi
dengan hardware.) Misalnya, anggaplah ada LinearLayout yang berisi
ListView di atas Button. Daftar tampilan untuk
LinearLayout akan terlihat seperti ini:
DrawDisplayList(ListView)DrawDisplayList(Button)
Sekarang asumsikan Anda ingin mengubah opasitas ListView. Setelah
memanggil setAlpha(0.5f) di ListView, daftar display sekarang
berisi:
SaveLayerAlpha(0.5)DrawDisplayList(ListView)RestoreDrawDisplayList(Button)
Kode gambar kompleks di ListView tidak dieksekusi. Sebagai gantinya, sistem hanya memperbarui daftar tampilan LinearLayout yang jauh lebih sederhana.
Dalam aplikasi yang tidak mengaktifkan akselerasi hardware, kode gambar baik untuk daftar maupun induknya akan dieksekusi lagi.
Dukungan untuk operasi gambar
Jika akselerasi hardware diaktifkan, pipeline rendering 2D mendukung operasi gambar Canvas yang paling umum digunakan, serta banyak operasi lain yang jarang digunakan. Semua operasi gambar yang digunakan untuk merender aplikasi yang disertakan dengan Android, widget dan tata letak default, serta efek visual lanjutan yang umum seperti pantulan dan tekstur berubin, juga didukung.
Tabel berikut menjelaskan tingkat dukungan berbagai operasi di tingkat API:
| Level API pertama yang didukung | ||||
| Canvas | ||||
| drawBitmapMesh() (array warna) | 18 | |||
| drawPicture() | 23 | |||
| drawPosText() | 16 | |||
| drawTextOnPath() | 16 | |||
| drawVertices() | 29 | |||
| setDrawFilter() | 16 | |||
| clipPath() | 18 | |||
| clipRegion() | 18 | |||
| clipRect(Region.Op.XOR) | 18 | |||
| clipRect(Region.Op.Difference) | 18 | |||
| clipRect(Region.Op.ReverseDifference) | 18 | |||
| clipRect() dengan rotasi/perspektif | 18 | |||
| Paint | ||||
| setAntiAlias() (untuk teks) | 18 | |||
| setAntiAlias() (untuk garis) | 16 | |||
| setFilterBitmap() | 17 | |||
| setLinearText() | ✗ | |||
| setMaskFilter() | ✗ | |||
| setPathEffect() (untuk garis) | 28 | |||
| setShadowLayer() (selain teks) | 28 | |||
| setStrokeCap() (untuk garis) | 18 | |||
| setStrokeCap() (untuk titik) | 19 | |||
| setSubpixelText() | 28 | |||
| Xfermode | ||||
| PorterDuff.Mode.DARKEN (framebuffer) | 28 | |||
| PorterDuff.Mode.LIGHTEN (framebuffer) | 28 | |||
| PorterDuff.Mode.OVERLAY (framebuffer) | 28 | |||
| Shader | ||||
| ComposeShader di dalam ComposeShader | 28 | |||
| Shader jenis yang sama di dalam ComposeShader | 28 | |||
| Matriks lokal di ComposeShader | 18 | |||
Penskalaan kanvas
Pipeline rendering 2D dengan akselerasi hardware mula-mula dibuat untuk mendukung gambar yang tidak diskalakan, karena beberapa operasi gambar mengalami penurunan kualitas secara signifikan pada nilai skala lebih tinggi. Operasi ini diimplementasikan saat tekstur yang digambar pada skala 1.0 diubah oleh GPU. Mulai level API 28, semua operasi gambar dapat diskalakan tanpa masalah.
Tabel berikut menunjukkan kapan implementasi diubah untuk menangani skala besar dengan benar:
| Operasi gambar yang akan diskalakan | Level API pertama yang didukung |
| drawText() | 18 |
| drawPosText() | 28 |
| drawTextOnPath() | 28 |
| Bentuk Sederhana | 17 |
| Bentuk Kompleks | 28 |
| drawPath() | 28 |
| Lapisan bayangan | 28 |
Jika aplikasi Anda terpengaruh oleh salah satu fitur atau batasan yang hilang ini, Anda dapat menonaktifkan akselerasi hardware hanya untuk bagian aplikasi yang terpengaruh dengan memanggil setLayerType(View.LAYER_TYPE_SOFTWARE, null).
Dengan begitu, Anda masih dapat memanfaatkan akselerasi hardware di tempat lain.
Lihat Mengontrol akselerasi hardware untuk mengetahui informasi selengkapnya tentang cara mengaktifkan dan menonaktifkan akselerasi hardware di berbagai tingkat dalam aplikasi Anda.
Lapisan tampilan
Di semua versi Android, tampilan dapat dirender menjadi buffer luar layar, baik menggunakan cache gambar tampilan, atau menggunakan
Canvas.saveLayer. Buffer luar-layar, atau lapisan, memiliki beberapa kegunaan. Anda
dapat menggunakannya untuk memperoleh performa yang lebih baik saat menganimasikan tampilan yang kompleks atau untuk menerapkan
efek komposisi. Misalnya, Anda dapat mengimplementasikan efek pudar menggunakan
Canvas.saveLayer untuk merender tampilan menjadi lapisan untuk sementara, lalu menyusunnya
kembali di layar dengan faktor opasitas.
Mulai dari Android 3.0 (level API 11), Anda dapat menggunakan metode View.setLayerType untuk mengontrol bagaimana dan kapan lapisan digunakan. API ini memerlukan dua
parameter: jenis lapisan yang ingin digunakan dan objek Paint
opsional yang menjelaskan bagaimana lapisan harus disusun. Anda dapat menggunakan parameter
Paint untuk menerapkan filter warna, mode penggabungan khusus, atau
opasitas ke sebuah lapisan. Sebuah tampilan dapat menggunakan salah satu dari tiga jenis lapisan:
LAYER_TYPE_NONE: Tampilan dirender secara normal dan tidak ditunjang oleh buffer luar-layar. Ini merupakan perilaku default.LAYER_TYPE_HARDWARE: Tampilan dirender di hardware menjadi tekstur hardware jika aplikasi mengaktifkan akselerasi hardware. Jika aplikasi tidak mengaktifkan akselerasi hardware, jenis lapisan ini berperilaku sama sepertiLAYER_TYPE_SOFTWARE.LAYER_TYPE_SOFTWARE: Tampilan dirender di software menjadi bitmap.
Jenis lapisan yang Anda gunakan bergantung pada sasaran Anda:
Performa: Gunakan jenis lapisan hardware untuk merender tampilan menjadi tekstur hardware. Setelah tampilan dirender menjadi lapisan, kode gambarnya tidak perlu dijalankan hingga tampilan tersebut memanggil
invalidate. Beberapa animasi, seperti animasi alfa, dapat langsung diterapkan ke lapisan, yang sangat efisien jika ditangani oleh GPU.Efek visual: Gunakan jenis lapisan hardware atau software dan
Paintuntuk menerapkan perlakuan visual khusus ke tampilan. Misalnya, Anda dapat menggambar tampilan secara hitam putih menggunakanColorMatrixColorFilter.Kompatibilitas: Gunakan jenis lapisan software untuk memaksa tampilan dirender di software. Jika tampilan yang diakselerasi dengan hardware (misalnya, jika seluruh aplikasi Anda diakselerasi dengan hardware) mengalami masalah rendering, ini merupakan cara mudah untuk mengatasi keterbatasan pipeline rendering hardware.
Lapisan tampilan dan animasi
Lapisan hardware dapat menghasilkan animasi yang lebih cepat dan lebih halus jika aplikasi Anda
menggunakan akselerasi hardware. Menjalankan animasi pada kecepatan 60 frame per detik tidak selalu dimungkinkan saat menganimasikan tampilan kompleks yang menghasilkan banyak operasi gambar. Hal ini dapat diatasi dengan menggunakan lapisan hardware untuk merender tampilan menjadi tekstur hardware. Selanjutnya, tekstur hardware dapat digunakan untuk menganimasikan
tampilan, sehingga tampilan tidak perlu terus-menerus menggambar ulang dirinya sendiri saat
sedang dianimasikan. Tampilan tidak digambar ulang kecuali jika Anda mengubah properti tampilan, yang akan memanggil invalidate, atau jika Anda memanggil invalidate secara manual. Jika
Anda menjalankan animasi dalam aplikasi dan hasilnya tidak sehalus yang diinginkan, cobalah mengaktifkan lapisan hardware di tampilan teranimasi.
Jika tampilan ditunjang oleh lapisan hardware, beberapa propertinya akan ditangani saat lapisan disusun di layar. Menetapkan properti ini juga merupakan langkah yang efisien karena dengan demikian tampilan tidak perlu dibatalkan dan digambar ulang. Berikut adalah daftar properti yang memengaruhi cara lapisan disusun. Memanggil setter untuk salah satu properti ini akan menghasilkan pembatalan yang optimal dan meniadakan keharusan untuk menggambar ulang tampilan yang ditargetkan:
alpha: Mengubah opasitas lapisanx,y,translationX,translationY: Mengubah posisi lapisanscaleX,scaleY: Mengubah ukuran lapisanrotation,rotationX,rotationY: Mengubah orientasi lapisan dalam ruang 3DpivotX,pivotY: Mengubah asal transformasi lapisan
Properti ini adalah nama yang digunakan saat menganimasikan tampilan dengan
ObjectAnimator. Jika Anda ingin mengakses properti ini, panggil
setter atau getter yang sesuai. Misalnya, untuk mengubah properti alfa, panggil
setAlpha. Cuplikan kode berikut menunjukkan cara paling efisien untuk memutar tampilan secara 3D di seputar sumbu Y:
Kotlin
view.setLayerType(View.LAYER_TYPE_HARDWARE, null) ObjectAnimator.ofFloat(view, "rotationY", 180f).start()
Java
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator.ofFloat(view, "rotationY", 180).start();
Karena lapisan hardware menggunakan memori video, sebaiknya Anda mengaktifkannya hanya selama durasi animasi, lalu menonaktifkannya setelah animasi selesai. Anda dapat melakukannya melalui pemroses animasi:
Kotlin
view.setLayerType(View.LAYER_TYPE_HARDWARE, null) ObjectAnimator.ofFloat(view, "rotationY", 180f).apply { addListener(object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { view.setLayerType(View.LAYER_TYPE_NONE, null) } }) start() }
Java
view.setLayerType(View.LAYER_TYPE_HARDWARE, null); ObjectAnimator animator = ObjectAnimator.ofFloat(view, "rotationY", 180); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { view.setLayerType(View.LAYER_TYPE_NONE, null); } }); animator.start();
Untuk informasi selengkapnya tentang animasi properti, lihat Animasi properti.
Tips dan trik
Peralihan ke grafis 2D dengan akselerasi hardware dapat meningkatkan performa dengan seketika, tetapi Anda tetap harus mendesain aplikasi agar menggunakan GPU secara efektif dengan mengikuti rekomendasi berikut:
- Kurangi jumlah tampilan di aplikasi Anda
- Semakin banyak tampilan yang harus digambar oleh sistem, semakin lambat pemuatannya. Hal ini juga berlaku untuk pipeline rendering software. Mengurangi jumlah tampilan adalah salah satu cara termudah untuk mengoptimalkan UI Anda.
- Hindari overdraw
- Jangan menggambar terlalu banyak lapisan yang bertumpuk satu di atas yang lain. Hapus tampilan yang sepenuhnya terhalang oleh tampilan buram lainnya di atasnya. Jika Anda perlu menggambar beberapa lapisan yang dipadukan satu di atas yang lain, pertimbangkan untuk menggabungkannya menjadi satu lapisan. Salah satu aturan praktis terkait hardware saat ini adalah jangan menggambar lebih dari 2,5 kali lipat jumlah piksel di layar per frame (piksel transparan dalam jumlah bitmap!).
- Jangan membuat objek render dalam metode gambar
- Kesalahan yang umum adalah membuat
Paintbaru atauPathbaru setiap kali metode rendering dipanggil. Hal ini memaksa pembersih sampah memori untuk berjalan lebih sering dan juga mengabaikan cache dan pengoptimalan di pipeline hardware. - Jangan terlalu sering mengubah bentuk
- Bentuk, jalur, dan lingkaran yang kompleks, misalnya, dirender menggunakan mask tekstur. Setiap kali Anda membuat atau mengubah jalur, pipeline hardware akan membuat mask baru, dan ini dapat memerlukan banyak biaya.
- Jangan terlalu sering mengubah bitmap
- Setiap kali Anda mengubah konten bitmap, bitmap akan diupload lagi sebagai tekstur GPU saat Anda menggambarnya lagi.
- Gunakan alfa dengan hati-hati
- Saat Anda membuat tampilan transparan menggunakan
setAlpha,AlphaAnimation, atauObjectAnimator, tampilan akan dirender di buffer luar-layar yang menggandakan rasio pengisian yang diperlukan. Saat menerapkan alfa di tampilan yang sangat besar, sebaiknya setel jenis lapisan tampilan keLAYER_TYPE_HARDWARE.