Bagian ini menunjukkan cara men-debug Aplikasi Tidak Merespons (ANR) menggunakan
ProfilingManager dengan contoh rekaman aktivitas.
Menyiapkan aplikasi untuk mengumpulkan ANR
Mulailah 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 rekaman aktivitas tersebut di Perfetto UI.
Menganalisis rekaman aktivitas
Karena ANR memicu pelacakan, Anda tahu bahwa pelacakan berakhir saat sistem mendeteksi ketidakresponsifan di thread utama aplikasi Anda. 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 menampilkan 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. Kueri ini menggunakan waktu CPU selama 1,48 detik (Gambar 3).
Jika Anda hanya mengandalkan pelacakan tumpukan pada saat ANR untuk proses debug, Anda
mungkin salah mengatribusikan ANR sepenuhnya ke kode yang berjalan dalam
slice rekaman aktivitas handleNetworkResponse yang belum berakhir saat profil
selesai direkam. Namun,
1,48 detik tidak cukup untuk memicu ANR dengan sendirinya, meskipun ini adalah
operasi yang mahal. Anda perlu melihat lebih jauh ke belakang untuk memahami apa yang
memblokir thread utama sebelum metode ini.
Untuk mendapatkan titik awal guna mencari penyebab ANR, kita mulai mencari
setelah frame terakhir yang dihasilkan oleh UI thread yang sesuai dengan
slice Choreographer#doFrame 551275 dan tidak ada sumber penundaan yang besar sebelum
memulai slice MyApp:SubmitButton yang berakhir di ANR (Gambar 4).
Untuk memahami pemblokiran, perkecil untuk memeriksa seluruh slice
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 polanya.
Tiga interval tidur pertama (Gambar 6–8) hampir identik, masing-masing sekitar 2 detik. Sleep keempat yang merupakan pencilan (Gambar 9) adalah 0,7 detik. Durasi tepat 2 detik jarang terjadi secara kebetulan di lingkungan komputasi. Hal ini sangat menunjukkan waktu tunggu yang diprogram, bukan pertentangan resource acak. Tidur terakhir mungkin disebabkan oleh thread yang menyelesaikan penantiannya karena operasi yang ditunggunya berhasil.
Hipotesisnya adalah aplikasi mencapai waktu tunggu yang ditentukan pengguna selama 2 detik beberapa kali dan akhirnya berhasil, sehingga menyebabkan penundaan yang cukup untuk memicu ANR.
Untuk memverifikasi hal ini, periksa kode yang terkait dengan bagian trace 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) { // ... } void prepareNetworkRequest() { // ... } public void handleNetworkResponse() { Trace.beginSection("handleNetworkResponse"); // ... Trace.endSection(); }
Kode ini mengonfirmasi hipotesis tersebut. Metode onClickSubmit menjalankan permintaan jaringan
pada UI thread dengan NETWORK_TIMEOUT_MILLISECS hardcode sebesar 2.000 md.
Yang terpenting, fungsi ini berjalan di dalam loop while yang mencoba ulang 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).
Upaya keempat berhasil setelah 0,7 detik, sehingga kode dapat melanjutkan ke
handleNetworkResponse. Namun, akumulasi waktu tunggu telah 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 jenis ANR ini.