Mulai dari Android 3.0 (API level 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 API level target Anda adalah >=14, tetapi juga
dapat diaktifkan secara eksplisit. Jika aplikasi Anda hanya menggunakan tampilan standar dan
Drawable
, mengaktifkan akselerasi hardware secara global seharusnya tidak akan menimbulkan efek gambar
yang merugikan. Namun, karena tidak semua operasi gambar 2D mendukung
akselerasi hardware, mengaktifkan akselerasi hardware mungkin akan memengaruhi beberapa tampilan kustom atau panggilan gambar Anda. Masalah ini
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 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);
Catatan: Saat ini Anda tidak dapat menonaktifkan akselerasi hardware di tingkat jendela.
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);
Catatan: Saat ini Anda tidak dapat mengaktifkan akselerasi hardware di tingkat tampilan. Lapisan tampilan memiliki fungsi lain selain menonaktifkan akselerasi hardware. Lihat Lapisan tampilan untuk informasi lebih lanjut mengenai penggunaannya.
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.isHardwareAccelerated()
menampilkantrue
jikaView
dihubungkan ke jendela dengan akselerasi hardware.Canvas.isHardwareAccelerated()
menampilkantrue
jikaCanvas
diakselerasi 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 model gambar 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, model gambar ini
memiliki dua kelemahan:
- Pertama, model ini memerlukan eksekusi banyak kode di setiap operasi gambar. Misalnya, jika
aplikasi Anda memanggil
invalidate()
di 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
invalidate()
tidak 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. Karena itu, Anda harus selalu memanggilinvalidate()
di tampilan kustom setiap kali Anda memodifikasi data atau status yang memengaruhi kode gambar tampilan tersebut.
Catatan: Tampilan Android otomatis memanggil invalidate()
ketika propertinya berubah, seperti warna
latar belakang atau teks dalam TextView
.
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. Bukannya menjalankan perintah menggambar 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 display untuk tampilan yang ditandai sebagai area kotor oleh panggilan
invalidate()
. Tampilan yang belum dibatalkan dapat digambar ulang cukup 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 sistem Android mencatat daftar tampilan
suatu tampilan, Anda harus memanggil invalidate()
. Jika Anda
tidak melakukannya, tampilan akan terlihat sama meskipun telah diubah.
Penggunaan daftar display 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 pada tampilan dengan daftar tampilan (tampilan ketika
aplikasi mengaktifkan akselerasi hardware.) Misalnya, anggaplah terdapat sebuah LinearLayout
yang berisi ListView
di atas sebuah Button
. Daftar display 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)
- Restore
- DrawDisplayList(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 API level 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 |
Catatan: Bentuk 'Sederhana' adalah perintah drawRect()
,
drawCircle()
, drawOval()
, drawRoundRect()
, dan
drawArc()
(dengan useCenter=false) yang diterbitkan dengan Paint yang tidak memiliki
PathEffect, dan tidak berisi gabungan non-default (via setStrokeJoin()
/
setStrokeMiter()
). Contoh lain dari perintah gambar ini termasuk dalam 'Kompleks', dalam
diagram di atas.
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 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 fade menggunakan Canvas.saveLayer()
untuk merender tampilan
menjadi lapisan untuk sementara, lalu menyusunnya kembali di layar dengan faktor opasitas.
Mulai dari Android 3.0 (API level 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 buffering off-screen. 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
sampai 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
Paint
untuk 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
yang 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
animasi tampilan.
Jika tampilan ditunjang oleh lapisan hardware, beberapa propertinya akan ditangani saat lapisan disusun di layar. Menyetel properti ini juga merupakan langkah yang efisien karena dengan demikian tampilan tidak perlu dibatalkan dan digambar ulang. Daftar properti berikut memengaruhi penyusunan lapisan. 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
Paint
baru atauPath
baru 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
.