Saat UI thread aplikasi Android diblokir terlalu lama, error "Aplikasi Tidak Merespons" (ANR) akan dipicu. Jika aplikasi digunakan di latar depan, sistem menampilkan dialog kepada pengguna, seperti ditunjukkan di gambar 1. Dialog ANR memberikan kesempatan kepada pengguna untuk memaksa menghentikan aplikasi.
ANR adalah masalah yang muncul karena thread utama aplikasi, yang bertanggung jawab untuk mengupdate UI, tidak dapat memproses peristiwa input pengguna atau melakukan draw, sehingga dapat membuat pengguna kebingungan. Untuk mengetahui informasi selengkapnya tentang thread utama aplikasi, lihat Proses dan thread.
ANR dipicu untuk aplikasi jika salah satu kondisi berikut terjadi:
- Waktu pengiriman input habis: Jika aplikasi Anda tidak merespons peristiwa input (seperti penekanan tombol atau sentuhan layar) dalam 5 detik.
- Mengeksekusi layanan: Jika layanan yang dideklarasikan oleh aplikasi Anda tidak dapat menyelesaikan
eksekusi
Service.onCreate()
danService.onStartCommand()
/Service.onBind()
dalam beberapa detik. - Service.startForeground() tidak dipanggil: Jika aplikasi Anda menggunakan
Context.startForegroundService()
untuk memulai layanan baru di latar depan, tetapi layanan tidak akan memanggilstartForeground()
dalam 5 detik. - Broadcast intent: Jika
BroadcastReceiver
belum selesai dieksekusi dalam jangka waktu yang ditentukan. Jika aplikasi memiliki aktivitas di latar depan, waktu tunggu ini adalah 5 detik. - Interaksi JobScheduler: Jika
JobService
tidak ditampilkan dariJobService.onStartJob()
atauJobService.onStopJob()
dalam beberapa detik, atau jika tugas yang dimulai pengguna dijalankan dan aplikasi Anda tidak memanggilJobService.setNotification()
dalam beberapa detik setelahJobService.onStartJob()
dipanggil. Untuk aplikasi yang menargetkan Android 13 dan yang lebih lama, ANR bersifat implisit dan tidak dilaporkan ke aplikasi. Untuk aplikasi yang menargetkan Android 14 dan yang lebih baru, ANR bersifat eksplisit dan dilaporkan ke aplikasi.
Jika aplikasi mengalami ANR, Anda dapat menggunakan pedoman di artikel ini untuk mendiagnosis dan memperbaiki masalahnya.
Mendeteksi masalah
Jika sudah memublikasikan aplikasi, Anda dapat menggunakan Android vitals untuk melihat informasi ANR untuk aplikasi Anda. Anda dapat menggunakan alat lain untuk mendeteksi ANR di kolom, tetapi perhatikan bahwa alat pihak ketiga tidak dapat melaporkan ANR di Android versi lama (Android 10 dan yang lebih lama), tidak seperti Android vitals.
Android vitals
Android vitals dapat membantu Anda memantau dan meningkatkan rasio ANR aplikasi. Android vitals mengukur beberapa rasio ANR:
- Rasio ANR: Persentase pengguna aktif harian Anda yang mengalami semua jenis ANR.
- Rasio ANR yang dirasakan pengguna: Persentase pengguna aktif harian
yang mengalami setidaknya satu ANR yang dirasakan pengguna. Saat ini, hanya ANR
jenis
Input dispatching timed out
yang dianggap dirasakan pengguna. - Rasio multi-ANR: Persentase pengguna aktif harian yang mengalami setidaknya dua ANR.
Pengguna aktif harian adalah pengguna unik yang menggunakan aplikasi Anda pada satu hari di satu perangkat, kemungkinan melalui beberapa sesi. Jika pengguna menggunakan aplikasi Anda di lebih dari satu perangkat dalam satu hari, setiap perangkat akan berkontribusi pada jumlah pengguna aktif untuk hari tersebut. Jika beberapa pengguna menggunakan perangkat yang sama dalam satu hari, ini akan dihitung sebagai satu pengguna aktif.
Rasio ANR yang dirasakan pengguna merupakan data vital inti yang berarti rasio ini memengaruhi visibilitas aplikasi Anda di Google Play. Rasio ini penting karena ANR yang dihitung selalu terjadi saat pengguna berinteraksi dengan aplikasi, sehingga paling banyak menyebabkan gangguan.
Play telah menentukan dua batas perilaku buruk pada metrik ini:
- Batas perilaku buruk keseluruhan: Setidaknya 0,47% pengguna aktif harian mengalami ANR yang dirasakan pengguna di semua model perangkat.
- Batas perilaku buruk per perangkat: Setidaknya 8% pengguna aktif harian mengalami ANR yang dirasakan pengguna, untuk satu model perangkat.
Jika aplikasi Anda melebihi batas perilaku buruk secara keseluruhan, mungkin aplikasi tersebut akan sulit ditemukan di semua perangkat. Jika aplikasi Anda melebihi batas perilaku buruk per perangkat di beberapa perangkat, mungkin aplikasi tersebut akan sulit ditemukan di perangkat tersebut, dan peringatan mungkin ditampilkan di listingan Play Store Anda.
Android vitals dapat memperingatkan Anda melalui Konsol Play saat aplikasi Anda menunjukkan ANR yang berlebihan.
Untuk informasi tentang cara Google Play mengumpulkan data Android vitals, lihat dokumentasi Konsol Play.
Mendiagnosis ANR
Ada beberapa pola umum yang perlu diperhatikan saat mendiagnosis ANR:
- Aplikasi melakukan operasi lambat yang melibatkan I/O di thread utama.
- Aplikasi melakukan perhitungan yang lama di thread utama.
- Thread utama melakukan panggilan pengikat sinkron ke proses lain, dan proses lain tersebut memerlukan waktu yang lama untuk kembali.
- Thread utama diblokir menunggu blok yang disinkronkan untuk operasi panjang yang terjadi di thread lain.
- Thread utama mengalami deadlock dengan thread lain, baik dalam proses Anda maupun melalui panggilan binder. Thread utama tidak hanya menunggu operasi yang panjang selesai, tetapi dalam situasi deadlock. Untuk mengetahui informasi selengkapnya, lihat Deadlock di Wikipedia.
Teknik berikut dapat membantu Anda menentukan penyebab ANR.
HealthStats
HealthStats
memberikan metrik tentang
kesehatan aplikasi dengan mencatat total waktu pengguna dan sistem, waktu CPU,
jaringan, statistik radio, waktu aktif/nonaktif layar, dan alarm bangun. Hal ini dapat
membantu mengukur penggunaan CPU dan konsumsi baterai secara keseluruhan.
Debug
Debug
membantu memeriksa aplikasi Android
selama pengembangan, termasuk jumlah rekaman aktivitas dan alokasi untuk mengidentifikasi jank
dan jeda dalam aplikasi. Anda juga dapat menggunakan Debug
untuk mendapatkan penghitung memori native
dan runtime, serta metrik memori yang dapat membantu mengidentifikasi jejak memori
proses tertentu.
ApplicationExitInfo
ApplicationExitInfo
tersedia
di Android 11 (API level 30) atau lebih tinggi, dan memberikan informasi tentang
alasan untuk keluar dari aplikasi. Ini mencakup ANR, memori rendah, error aplikasi,
penggunaan CPU yang berlebihan, gangguan pengguna, gangguan sistem, atau perubahan
izin runtime.
Strict Mode
Menggunakan StrictMode
membantu Anda menemukan
operasi I/O yang tidak disengaja di thread utama saat Anda mengembangkan aplikasi.
Anda dapat menggunakan StrictMode
pada tingkat aplikasi atau aktivitas.
Mengaktifkan dialog ANR latar belakang
Android menampilkan dialog ANR untuk aplikasi yang memerlukan waktu terlalu lama untuk memproses pesan siaran hanya jika Tampilkan semua ANR diaktifkan di Opsi developer pada perangkat. Karena alasan ini, dialog ANR latar belakang tidak selalu ditampilkan kepada pengguna, tetapi aplikasi tetap dapat mengalami masalah performa.
Traceview
Anda dapat menggunakan Traceview untuk mendapatkan rekaman aktivitas aplikasi yang sedang berjalan saat memeriksa kasus penggunaan dan mengidentifikasi tempat terjadinya kesibukan thread utama. Untuk mengetahui informasi tentang cara menggunakan Traceview, lihat Pemrofilan dengan Traceview dan dmtracedump.
Mengambil file rekaman aktivitas
Android menyimpan informasi rekaman aktivitas saat mengalami ANR. Di OS versi lama
, ada satu file /data/anr/traces.txt
di perangkat.
Di OS versi baru, ada beberapa file /data/anr/anr_*
.
Anda dapat mengakses rekaman aktivitas ANR dari perangkat atau emulator dengan menggunakan
Android Debug Bridge (adb) sebagai root:
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
Anda dapat mengambil laporan bug dari perangkat fisik dengan menggunakan opsi developer Ambil laporan bug di perangkat, atau perintah adb bugreport pada mesin pengembangan. Untuk mengetahui informasi selengkapnya, lihat Merekam dan membaca laporan bug.
Memperbaiki masalah
Setelah mengidentifikasi masalah, Anda dapat menggunakan tips di bagian ini untuk memperbaiki masalah yang umum ditemukan.
Kode lambat di thread utama
Identifikasi tempat di kode tempat thread utama aplikasi sibuk selama lebih dari 5 detik. Cari kasus penggunaan yang mencurigakan di aplikasi dan coba reproduksi ANR.
Misalnya, Gambar 2 menunjukkan linimasa Traceview tempat thread utama sibuk selama lebih dari 5 detik.
Gambar 2 menunjukkan bahwa sebagian besar kode yang mengganggu terjadi di pengendali
onClick(View)
,
seperti yang ditunjukkan dalam contoh kode berikut:
Kotlin
override fun onClick(v: View) { // This task runs on the main thread. BubbleSort.sort(data) }
Java
@Override public void onClick(View view) { // This task runs on the main thread. BubbleSort.sort(data); }
Dalam kasus ini, Anda harus memindahkan pekerjaan yang berjalan di thread utama ke thread pekerja. Framework Android menyertakan class yang dapat membantu memindahkan tugas ke thread pekerja. Lihat Thread pekerja untuk informasi selengkapnya.
IO di thread utama
Mengeksekusi operasi IO di thread utama adalah penyebab umum operasi yang lambat di thread utama, yang dapat menyebabkan ANR. Sebaiknya pindahkan semua operasi IO ke thread pekerja, seperti yang ditunjukkan di bagian sebelumnya.
Beberapa contoh operasi IO adalah operasi jaringan dan penyimpanan. Untuk mengetahui informasi selengkapnya, lihat Melakukan operasi jaringan dan Menyimpan data.
Pertentangan kunci
Di beberapa skenario, pekerjaan yang menyebabkan ANR tidak secara langsung dieksekusi di thread utama aplikasi. Jika thread pekerja menahan kunci pada resource yang diperlukan oleh thread utama untuk menyelesaikan pekerjaannya, ANR mungkin terjadi.
Misalnya, gambar 3 menunjukkan linimasa Traceview dengan sebagian besar pekerjaan yang dijalankan di thread pekerja.
Gambar 3. Linimasa Traceview yang menunjukkan pekerjaan yang sedang dieksekusi di thread pekerja
Namun, jika pengguna masih mengalami ANR, sebaiknya lihat status
thread utama di Android Device Monitor. Biasanya, thread utama akan berada dalam status
RUNNABLE
jika sudah siap untuk mengupdate UI dan umumnya responsif.
Namun, jika thread utama tidak dapat melanjutkan eksekusi,
statusnya BLOCKED
dan tidak dapat merespons peristiwa. Status ditunjukkan di Android Device Monitor sebagai
Monitor atau Wait, seperti ditunjukkan di gambar 5.
Trace berikut menunjukkan thread utama aplikasi yang diblokir menunggu resource:
...
AsyncTask #2" prio=5 tid=18 Runnable
| group="main" sCount=0 dsCount=0 obj=0x12c333a0 self=0x94c87100
| sysTid=25287 nice=10 cgrp=default sched=0/0 handle=0x94b80920
| state=R schedstat=( 0 0 0 ) utm=757 stm=0 core=3 HZ=100
| stack=0x94a7e000-0x94a80000 stackSize=1038KB
| held mutexes= "mutator lock"(shared held)
at com.android.developer.anrsample.BubbleSort.sort(BubbleSort.java:8)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:147)
- locked <0x083105ee> (a java.lang.Boolean)
at com.android.developer.anrsample.MainActivity$LockTask.doInBackground(MainActivity.java:135)
at android.os.AsyncTask$2.call(AsyncTask.java:305)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
...
Meninjau trace dapat membantu Anda mencari kode yang memblokir thread utama. Kode berikut bertanggung jawab untuk memegang kunci yang memblokir thread utama di trace sebelumnya:
Kotlin
override fun onClick(v: View) { // The worker thread holds a lock on lockedResource LockTask().execute(data) synchronized(lockedResource) { // The main thread requires lockedResource here // but it has to wait until LockTask finishes using it. } } class LockTask : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? = synchronized(lockedResource) { // This is a long-running operation, which makes // the lock last for a long time BubbleSort.sort(params[0]) } }
Java
@Override public void onClick(View v) { // The worker thread holds a lock on lockedResource new LockTask().execute(data); synchronized (lockedResource) { // The main thread requires lockedResource here // but it has to wait until LockTask finishes using it. } } public class LockTask extends AsyncTask<Integer[], Integer, Long> { @Override protected Long doInBackground(Integer[]... params) { synchronized (lockedResource) { // This is a long-running operation, which makes // the lock last for a long time BubbleSort.sort(params[0]); } } }
Contoh lain adalah thread utama aplikasi yang menunggu hasil dari
thread pekerja, seperti yang ditunjukkan dalam kode berikut. Perlu diperhatikan bahwa penggunaan wait()
dan
notify()
bukan pola yang direkomendasikan di Kotlin, yang memiliki mekanismenya sendiri
untuk menangani kondisi serentak. Saat menggunakan Kotlin, sebaiknya gunakan mekanisme khusus Kotlin
jika memungkinkan.
Kotlin
fun onClick(v: View) { val lock = java.lang.Object() val waitTask = WaitTask(lock) synchronized(lock) { try { waitTask.execute(data) // Wait for this worker thread’s notification lock.wait() } catch (e: InterruptedException) { } } } internal class WaitTask(private val lock: java.lang.Object) : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? { synchronized(lock) { BubbleSort.sort(params[0]) // Finished, notify the main thread lock.notify() } } }
Java
public void onClick(View v) { WaitTask waitTask = new WaitTask(); synchronized (waitTask) { try { waitTask.execute(data); // Wait for this worker thread’s notification waitTask.wait(); } catch (InterruptedException e) {} } } class WaitTask extends AsyncTask<Integer[], Integer, Long> { @Override protected Long doInBackground(Integer[]... params) { synchronized (this) { BubbleSort.sort(params[0]); // Finished, notify the main thread notify(); } } }
Ada beberapa situasi lain yang dapat memblokir thread utama, termasuk
thread yang menggunakan Lock
,
Semaphore
,
serta kumpulan resource (seperti kumpulan koneksi database)
atau mekanisme pengecualian (mutex) lainnya.
Anda harus mengevaluasi kunci yang ditahan oleh aplikasi di resource secara umum, tetapi jika ingin menghindari ANR, sebaiknya lihat kunci yang ditahan untuk resource yang diperlukan oleh thread utama.
Pastikan bahwa kunci ditahan dalam waktu yang paling sedikit, atau
evaluasi apakah aplikasi perlu menahan kunci. Jika Anda menggunakan
kunci guna menentukan waktu untuk mengupdate UI berdasarkan pemrosesan thread pekerja,
gunakan mekanisme seperti
onProgressUpdate()
dan
onPostExecute()
untuk berkomunikasi antara thread pekerja dan utama.
Deadlock
Deadlock terjadi saat thread memasuki status menunggu karena resource yang diperlukan ditahan oleh thread lain, yang juga menunggu resource yang ditahan oleh thread pertama. Jika thread utama aplikasi mengalami situasi ini, ANR mungkin terjadi.
Deadlock adalah fenomena yang dipelajari dengan baik dalam ilmu komputer, dan terdapat algoritma pencegahan deadlock yang dapat Anda gunakan untuk menghindari deadlock.
Untuk mengetahui informasi selengkapnya, lihat Deadlock dan Algoritme pencegahan deadlock di Wikipedia.
Penerima siaran lambat
Aplikasi dapat merespons pesan siaran, seperti mengaktifkan atau menonaktifkan mode pesawat atau perubahan status konektivitas, melalui penerima siaran. ANR terjadi saat aplikasi memerlukan waktu yang terlalu lama untuk memproses pesan siaran.
ANR terjadi dalam kasus-kasus berikut:
- Penerima siaran belum selesai mengeksekusi metode
onReceive()
-nya dalam waktu yang cukup lama. - Penerima siaran memanggil
goAsync()
dan gagal memanggilfinish()
diPendingResult
.
Aplikasi Anda hanya boleh melakukan operasi pendek di
onReceive()
dari
BroadcastReceiver
.
Namun, jika aplikasi memerlukan pemrosesan
yang lebih kompleks karena pesan siaran, Anda harus mengalihkan tugas ke
IntentService
.
Anda dapat menggunakan alat seperti Traceview untuk mengidentifikasi apakah penerima siaran mengeksekusi operasi yang berjalan lama di thread utama aplikasi. Misalnya, Gambar 6 menunjukkan linimasa penerima siaran yang memproses pesan di thread utama selama sekitar 100 detik.
Perilaku ini dapat disebabkan oleh eksekusi operasi yang berjalan lama pada
metode
onReceive()
pada BroadcastReceiver
,
seperti yang ditunjukkan dalam contoh berikut:
Kotlin
override fun onReceive(context: Context, intent: Intent) { // This is a long-running operation BubbleSort.sort(data) }
Java
@Override public void onReceive(Context context, Intent intent) { // This is a long-running operation BubbleSort.sort(data); }
Dalam situasi seperti ini, sebaiknya pindahkan operasi yang berjalan lama ke
IntentService
karena menggunakan thread pekerja untuk mengeksekusi
pekerjaannya. Kode berikut menunjukkan cara menggunakan
IntentService
untuk memproses
operasi yang berjalan lama:
Kotlin
override fun onReceive(context: Context, intent: Intent) { Intent(context, MyIntentService::class.java).also { intentService -> // The task now runs on a worker thread. context.startService(intentService) } } class MyIntentService : IntentService("MyIntentService") { override fun onHandleIntent(intent: Intent?) { BubbleSort.sort(data) } }
Java
@Override public void onReceive(Context context, Intent intent) { // The task now runs on a worker thread. Intent intentService = new Intent(context, MyIntentService.class); context.startService(intentService); } public class MyIntentService extends IntentService { @Override protected void onHandleIntent(@Nullable Intent intent) { BubbleSort.sort(data); } }
Akibat menggunakan IntentService
, operasi yang berjalan lama
dieksekusi di thread pekerja, bukan di thread utama. Gambar 7
menunjukkan tugas yang dialihkan ke thread pekerja di linimasa Traceview.
Penerima siaran dapat menggunakan
goAsync()
untuk memberikan tanda kepada sistem bahwa diperlukan lebih banyak waktu untuk memproses pesan. Akan tetapi,
Anda harus memanggil
finish()
pada
objek
PendingResult
. Contoh berikut menunjukkan cara memanggil finish() untuk mengizinkan sistem
mendaur ulang penerima siaran dan menghindari ANR:
Kotlin
val pendingResult = goAsync() object : AsyncTask<Array<Int>, Int, Long>() { override fun doInBackground(vararg params: Array<Int>): Long? { // This is a long-running operation BubbleSort.sort(params[0]) pendingResult.finish() return 0L } }.execute(data)
Java
final PendingResult pendingResult = goAsync(); new AsyncTask<Integer[], Integer, Long>() { @Override protected Long doInBackground(Integer[]... params) { // This is a long-running operation BubbleSort.sort(params[0]); pendingResult.finish(); } }.execute(data);
Namun, memindahkan kode dari penerima siaran lambat ke thread lain dan
menggunakan goAsync()
tidak akan memperbaiki ANR jika siaran berada di latar belakang.
Waktu tunggu ANR masih berlaku.
GameActivity
Library GameActivity
telah mengurangi ANR dalam
studi kasus game dan aplikasi yang ditulis
dalam C atau C++. Jika Anda mengganti aktivitas native yang ada dengan GameActivity
,
Anda dapat mengurangi pemblokiran UI thread dan mencegah terjadinya beberapa ANR.
Untuk mengetahui informasi selengkapnya tentang ANR, lihat Menjaga aplikasi Anda tetap responsif. Untuk mengetahui informasi selengkapnya tentang thread, lihat Performa threading.
Direkomendasikan untuk Anda
- Catatan: teks link ditampilkan saat JavaScript nonaktif
- Bangun berlebihan