Bagian ini menunjukkan cara men-debug Aplikasi Tidak Merespons (ANR) menggunakan
ProfilingManager dengan contoh rekaman aktivitas.
Menyiapkan aplikasi untuk mengumpulkan ANR
Mulai dengan menyiapkan pemicu ANR di aplikasi Anda:
public void addANRTrigger() { ProfilingManager profilingManager = getApplicationContext().getSystemService( ProfilingManager.class); List<ProfilingTrigger> triggers = new ArrayList<>(); ProfilingTrigger.Builder triggerBuilder = new ProfilingTrigger.Builder( ProfilingTrigger.TRIGGER_TYPE_ANR); triggers.add(triggerBuilder.build()); Executor mainExecutor = Executors.newSingleThreadExecutor(); Consumer<ProfilingResult> resultCallback = profilingResult -> { // Handle uploading trace to your back-end }; profilingManager.registerForAllProfilingResults(mainExecutor, resultCallback); profilingManager.addProfilingTriggers(triggers); }
Setelah merekam dan mengupload rekaman aktivitas ANR, buka di UI Perfetto.
Menganalisis rekaman aktivitas
Karena ANR memicu rekaman aktivitas, Anda tahu bahwa rekaman aktivitas berakhir saat sistem mendeteksi aplikasi Anda tidak merespons di thread utama. Gambar 1 menunjukkan cara membuka thread utama aplikasi Anda yang diberi tag sesuai dalam UI.
Akhir rekaman aktivitas cocok dengan stempel waktu ANR, seperti yang ditunjukkan pada Gambar 2.
Rekaman aktivitas juga menunjukkan operasi yang dijalankan aplikasi saat ANR terjadi.
Secara khusus, aplikasi menjalankan kode di slice rekaman aktivitas handleNetworkResponse. Slice
ini berada di dalam slice MyApp:SubmitButton. Proses ini menggunakan waktu CPU 1,48 detik (Gambar 3).
Jika hanya mengandalkan pelacakan tumpukan pada saat ANR terjadi untuk proses debug, Anda
mungkin salah mengatribusikan ANR sepenuhnya ke kode yang berjalan dalam
irisan rekaman aktivitas handleNetworkResponse yang belum berakhir saat profil
selesai merekam. Namun,
1,48 detik tidak cukup untuk memicu ANR dengan sendirinya, meskipun merupakan
operasi yang mahal. Anda perlu melihat lebih jauh ke belakang untuk memahami apa yang memblokir thread utama sebelum metode ini.
Untuk mendapatkan titik awal dalam mencari penyebab ANR, kita mulai mencari
setelah frame terakhir yang dihasilkan oleh thread UI yang sesuai dengan
irisan Choreographer#doFrame 551275 dan tidak ada sumber penundaan besar sebelum
memulai irisan MyApp:SubmitButton yang berakhir dengan ANR (Gambar 4).
Untuk memahami pemblokiran, perkecil tampilan untuk memeriksa seluruh irisan MyApp:SubmitButton. Anda akan melihat detail penting dalam status thread, seperti yang ditunjukkan pada
Gambar 4: thread menghabiskan 75% waktu (6,7 detik) dalam status Sleeping
dan hanya 24% waktu dalam status Running.
Hal ini menunjukkan bahwa penyebab utama ANR adalah menunggu, bukan komputasi. Periksa setiap kejadian tidur untuk menemukan pola.
Tiga interval tidur pertama (Gambar 6–8) hampir identik, masing-masing sekitar 2 detik. Tidur keempat yang tidak biasa (Gambar 9) adalah 0,7 detik. Durasi tepat 2 detik jarang merupakan kebetulan dalam lingkungan komputasi. Hal ini menunjukkan waktu tunggu yang diprogram, bukan persaingan sumber daya acak. Tidur terakhir mungkin disebabkan oleh thread yang menyelesaikan penungguannya karena operasi yang ditunggunya berhasil.
Hipotesis ini adalah bahwa aplikasi mencapai waktu tunggu yang ditentukan pengguna sebesar 2 detik beberapa kali dan akhirnya berhasil, sehingga menyebabkan penundaan yang cukup untuk memicu ANR.
Untuk memverifikasinya, periksa kode yang terkait dengan bagian rekaman aktivitas MyApp:SubmitButton:
private static final int NETWORK_TIMEOUT_MILLISECS = 2000; public void setupButtonCallback() { findViewById(R.id.submit).setOnClickListener(submitButtonView -> { Trace.beginSection("MyApp:SubmitButton"); onClickSubmit(); Trace.endSection(); }); } public void onClickSubmit() { prepareNetworkRequest(); boolean networkRequestSuccess = false; int maxAttempts = 10; while (!networkRequestSuccess && maxAttempts > 0) { networkRequestSuccess = performNetworkRequest(NETWORK_TIMEOUT_MILLISECS); maxAttempts--; } if (networkRequestSuccess) { handleNetworkResponse(); } } boolean performNetworkRequest(int timeoutMiliseconds) { // ... } // ... } public void handleNetworkResponse() { Trace.beginSection("handleNetworkResponse"); // ... Trace.endSection(); }
Kode tersebut mengonfirmasi hipotesis ini. Metode onClickSubmit menjalankan permintaan jaringan di UI thread dengan NETWORK_TIMEOUT_MILLISECS yang dikodekan secara permanen sebesar 2000 md.
Yang penting, fungsi ini berjalan di dalam loop while yang mencoba lagi hingga 10 kali.
Dalam rekaman aktivitas khusus ini, pengguna kemungkinan memiliki konektivitas jaringan yang buruk. Tiga upaya pertama gagal, sehingga menyebabkan tiga waktu tunggu 2 detik (total 6 detik).
Percobaan keempat berhasil setelah 0,7 detik, sehingga kode dapat dilanjutkan ke
handleNetworkResponse. Namun, waktu tunggu yang terakumulasi sudah memicu
ANR.
Hindari jenis ANR ini dengan menempatkan operasi terkait jaringan yang memiliki latensi bervariasi ke dalam thread latar belakang, bukan mengeksekusinya di thread utama. Hal ini memungkinkan UI tetap responsif meskipun konektivitas buruk, sehingga sepenuhnya menghilangkan class ANR ini.