Aplikasi Android mengalami error setiap kali terdapat penutupan tidak terduga yang disebabkan oleh
pengecualian atau sinyal yang tidak tertangani. Aplikasi yang ditulis menggunakan Java atau Kotlin
akan mengalami error jika menampilkan pengecualian yang tidak tertangani, yang ditunjukkan oleh
class Throwable
. Aplikasi
yang ditulis menggunakan kode mesin atau C++ akan mengalami error jika terdapat sinyal yang tidak
tertangani, seperti SIGSEGV
, selama eksekusinya.
Saat aplikasi mengalami error, Android menghentikan prosesnya dan menampilkan dialog yang memberi tahu pengguna bahwa aplikasi telah berhenti, sebagaimana ditunjukkan dalam gambar 1.
Suatu aplikasi dapat mengalami error meskipun tidak berjalan di latar depan. Komponen aplikasi apa pun, bahkan seperti penerima siaran atau penyedia konten yang berjalan di latar belakang, dapat menyebabkan aplikasi mengalami error. Error ini sering kali membingungkan pengguna karena mereka tidak berinteraksi secara aktif dengan aplikasi.
Jika aplikasi mengalami error, Anda dapat menggunakan panduan di halaman ini untuk mendiagnosis dan mengatasi masalah.
Mendeteksi masalah
Anda mungkin tidak selalu mengetahui bahwa pengguna mengalami error saat mereka menggunakan aplikasi Anda. Jika telah memublikasikan aplikasi, Anda dapat menggunakan Android vitals untuk melihat rasio error aplikasi Anda.
Android vitals
Android vitals dapat membantu Anda memantau dan meningkatkan rasio error aplikasi. Android vitals mengukur beberapa rasio error:
- Rasio error: Persentase pengguna aktif harian yang mengalami berbagai jenis {i>crash<i}.
Rasio error yang dirasakan pengguna: Persentase pengguna aktif harian Anda yang mengalami setidaknya satu error saat mereka aktif menggunakan aplikasi Anda (error yang dirasakan pengguna). Aplikasi dianggap aktif digunakan jika menampilkan aktivitas atau menjalankan layanan latar depan apa pun.
Rasio multi-error: Persentase pengguna aktif harian yang mengalami setidaknya dua error.
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 error yang dirasakan pengguna merupakan data vital inti, yang berarti rasio ini memengaruhi visibilitas aplikasi Anda di Google Play. Rasio ini penting karena error yang dihitung selalu terjadi saat pengguna berinteraksi dengan aplikasi sehingga paling banyak menyebabkan gangguan.
Play telah menentukan dua batas perilaku buruk pada metrik ini:
- Keseluruhan batas perilaku buruk: Setidaknya 1,09% pengguna aktif harian mengalami error yang dirasakan pengguna, di semua model perangkat.
- Batas perilaku buruk per perangkat: Setidaknya 8% pengguna aktif harian mengalami error 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 error yang berlebihan.
Untuk informasi tentang cara Google Play mengumpulkan data Android vitals, lihat dokumentasi Konsol Play.
Mendiagnosis error
Setelah Anda mengidentifikasi bahwa aplikasi Anda melaporkan error, langkah berikutnya adalah mendiagnosisnya. Mengatasi error mungkin akan sulit. Namun, jika Anda dapat mengidentifikasi akar masalah error, kemungkinan besar Anda akan dapat menemukan solusinya.
Ada banyak situasi yang dapat menyebabkan error pada aplikasi Anda. Beberapa alasannya jelas, seperti memeriksa nilai null atau string kosong, tetapi alasan lainnya tidak begitu jelas, seperti meneruskan argumen yang tidak valid ke API atau bahkan interaksi multithread yang kompleks.
Error di Android menyebabkan stack trace, yang merupakan ringkasan dari urutan fungsi bertingkat yang dipanggil dalam program Anda hingga error terjadi. Anda dapat melihat crash stack trace di Android vitals.
Cara membaca stack trace
Langkah pertama untuk mengatasi error adalah dengan mengidentifikasi tempat terjadinya error. Anda dapat menggunakan stack trace yang tersedia dalam detail laporan jika Anda menggunakan Konsol Play atau output alat logcat. Jika stack trace tidak tersedia, Anda harus mereproduksi error tersebut secara lokal, baik dengan menguji aplikasi secara manual maupun dengan menghubungi pengguna yang terkena dampak, serta mereproduksinya saat menggunakan logcat.
Rekaman aktivitas berikut menunjukkan contoh error pada aplikasi yang ditulis menggunakan bahasa pemrograman Java:
--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.developer.crashsample, PID: 3686
java.lang.NullPointerException: crash sample
at com.android.developer.crashsample.MainActivity$1.onClick(MainActivity.java:27)
at android.view.View.performClick(View.java:6134)
at android.view.View$PerformClick.run(View.java:23965)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6440)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:746)
--------- beginning of system
Stack trace menunjukkan dua informasi penting untuk men-debug error:
- Jenis pengecualian yang ditampilkan.
- Bagian kode tempat pengecualian ditampilkan.
Jenis pengecualian yang ditampilkan biasanya merupakan petunjuk yang sangat kuat tentang masalah yang
terjadi. Lihat apakah petunjuk tersebut berupa
IOException
,
OutOfMemoryError
,
atau yang lainnya, lalu temukan dokumentasi tentang class pengecualian.
Class, metode, file. dan nomor baris file sumber tempat pengecualian muncul ditampilkan di baris kedua stack trace. Untuk setiap fungsi yang telah dipanggil, baris lainnya menunjukkan situs panggilan sebelumnya (disebut frame stack). Dengan menjalankan stack dan memeriksa kode, Anda mungkin menemukan tempat yang meneruskan nilai yang salah. Jika kode Anda tidak muncul di stack trace, ada kemungkinan di suatu tempat Anda meneruskan parameter yang tidak valid ke operasi asinkron. Anda dapat mencari tahu apa yang terjadi dengan memeriksa setiap baris stack trace, menemukan class API apa pun yang digunakan, dan mengonfirmasi bahwa parameter yang Anda teruskan sudah benar, serta bahwa Anda memanggilnya dari tempat yang diizinkan.
Stack trace untuk aplikasi dengan kode C dan C++ berfungsi dengan cara yang sama.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/foo/bar:10/123.456/78910:user/release-keys'
ABI: 'arm64'
Timestamp: 2020-02-16 11:16:31+0100
pid: 8288, tid: 8288, name: com.example.testapp >>> com.example.testapp <<<
uid: 1010332
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
x0 0000007da81396c0 x1 0000007fc91522d4 x2 0000000000000001 x3 000000000000206e
x4 0000007da8087000 x5 0000007fc9152310 x6 0000007d209c6c68 x7 0000007da8087000
x8 0000000000000000 x9 0000007cba01b660 x10 0000000000430000 x11 0000007d80000000
x12 0000000000000060 x13 0000000023fafc10 x14 0000000000000006 x15 ffffffffffffffff
x16 0000007cba01b618 x17 0000007da44c88c0 x18 0000007da943c000 x19 0000007da8087000
x20 0000000000000000 x21 0000007da8087000 x22 0000007fc9152540 x23 0000007d17982d6b
x24 0000000000000004 x25 0000007da823c020 x26 0000007da80870b0 x27 0000000000000001
x28 0000007fc91522d0 x29 0000007fc91522a0
sp 0000007fc9152290 lr 0000007d22d4e354 pc 0000007cba01b640
backtrace:
#00 pc 0000000000042f89 /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::Crasher::crash() const)
#01 pc 0000000000000640 /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::runCrashThread())
#02 pc 0000000000065a3b /system/lib/libc.so (__pthread_start(void*))
#03 pc 000000000001e4fd /system/lib/libc.so (__start_thread)
Jika Anda tidak melihat class dan informasi level fungsi dalam stack trace native, Anda mungkin harus membuat file simbol debug native dan menguploadnya ke Konsol Google Play. Untuk informasi selengkapnya, lihat Men-deobfuscate crash stack trace. Untuk informasi umum tentang masalah pada native code, lihat Mendiagnosis masalah pada native code.
Tips untuk mereproduksi error
Anda mungkin tidak dapat mereproduksi masalah hanya dengan memulai emulator atau menghubungkan perangkat ke komputer. Lingkungan pengembangan cenderung memiliki lebih banyak resource, seperti bandwidth, memori, dan penyimpanan. Gunakan jenis pengecualian untuk menentukan resource yang langka, atau menemukan korelasi antara versi Android, jenis perangkat, atau versi aplikasi Anda.
Error memori
Jika memiliki
OutOfMemoryError
,
Anda dapat mulai membuat emulator dengan kapasitas memori rendah untuk digunakan dalam pengujian. Gambar
2 menunjukkan setelan AVD Manager tempat Anda dapat mengontrol jumlah memori di
perangkat.
Pengecualian jaringan
Karena pengguna sering berada di dalam atau di luar jangkauan jaringan seluler atau WiFi, pengecualian jaringan dalam aplikasi biasanya tidak boleh dianggap sebagai error, melainkan sebagai kondisi pengoperasian normal yang terjadi secara tidak terduga.
Jika Anda perlu mereproduksi pengecualian jaringan, seperti
UnknownHostException
,
coba aktifkan mode pesawat saat aplikasi Anda mencoba menggunakan
jaringan.
Opsi lainnya adalah mengurangi kualitas jaringan di emulator dengan
memilih emulasi kecepatan jaringan dan/atau penundaan jaringan. Anda dapat menggunakan setelan
Speed dan Latency di AVD Manager, atau memulai emulator
dengan flag -netdelay
dan -netspeed
, sebagaimana ditunjukkan dalam contoh
command line berikut:
emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
Contoh ini menetapkan penundaan 20 detik di semua permintaan jaringan serta kecepatan upload dan download 14,4 Kbps. Untuk informasi selengkapnya tentang opsi command line untuk emulator, lihat Memulai Emulator dari command line.
Membaca dengan logcat
Setelah melakukan langkah-langkah untuk mereproduksi error, Anda dapat menggunakan alat seperti
logcat
untuk mendapatkan
informasi selengkapnya.
Output logcat akan menunjukkan pesan log lain yang telah Anda cetak, beserta
pesan log lainnya dari sistem. Jangan lupa untuk menonaktifkan semua pernyataan
Log
tambahan yang telah Anda
tambahkan, karena mencetaknya akan memboroskan CPU dan baterai saat aplikasi Anda sedang
berjalan.
Mencegah error yang disebabkan oleh pengecualian pointer null
Pengecualian pointer null (diidentifikasi oleh jenis error runtime
NullPointerException
) terjadi saat Anda mencoba mengakses objek yang
null, biasanya dengan memanggil metode atau mengakses anggotanya. Pengecualian pointer
null adalah penyebab terbesar terjadinya error aplikasi di Google Play. Tujuan null
adalah untuk menunjukkan bahwa objek tidak ada—misalnya, objek belum
dibuat atau ditetapkan. Untuk menghindari pengecualian pointer null, Anda harus memastikan bahwa
referensi objek yang digunakan tidak null sebelum memanggil
metode pada referensi tersebut atau mencoba mengakses anggotanya. Jika referensi objek bernilai
null, tangani kasus ini dengan baik (misalnya, keluar dari metode sebelum melakukan
operasi apa pun pada referensi objek dan tulis informasi ke log debug).
Pemeriksaan null untuk setiap parameter masing-masing metode yang dipanggil akan merepotkan, Anda dapat mengandalkan IDE atau jenis objek untuk menandakan nullability.
Bahasa pemrograman Java
Bagian berikut berlaku untuk bahasa pemrograman Java.
Peringatan waktu kompilasi
Beri anotasi parameter metode Anda dan nilai yang ditampilkan dengan
@Nullable
dan
@NonNull
untuk menerima peringatan waktu
kompilasi dari IDE. Peringatan ini akan memberi tahu Anda bahwa objek bersifat nullable:
Pemeriksaan null ini ditujukan untuk objek yang Anda ketahui dapat bernilai null. Pengecualian pada
objek @NonNull
adalah indikasi error dalam kode yang perlu
ditangani.
Error waktu kompilasi
Karena nullability harus bermakna, Anda dapat menyematkannya dalam jenis yang Anda gunakan
sehingga ada pemeriksaan waktu kompilasi untuk null. Jika mengetahui bahwa objek dapat
bernilai null dan nullability tersebut harus ditangani, Anda dapat menggabungkannya dalam objek seperti
Optional
.
Anda harus selalu memilih jenis yang menyampaikan nullability.
Kotlin
Di Kotlin,
nullability
adalah bagian dari sistem jenis. Misalnya, variabel harus dideklarasikan sejak
awal sebagai nullable atau non-nullable. Jenis nullable ditandai dengan ?
:
// non-null
var s: String = "Hello"
// null
var s: String? = "Hello"
Variabel non-nullable tidak dapat diberi nilai null dan nullability variabel nullable harus diperiksa sebelum digunakan sebagai non-null.
Jika tidak ingin memeriksa null secara eksplisit, Anda dapat menggunakan operator panggilan
aman ?.
:
val length: Int? = string?.length // length is a nullable int
// if string is null, then length is null
Sebagai praktik terbaik, pastikan Anda menangani kasus null untuk objek nullable.
Jika tidak ditangani, aplikasi Anda dapat mengalami keadaan yang tidak diharapkan. Jika aplikasi Anda tidak lagi
mengalami error dengan NullPointerException
, Anda tidak akan mengetahui bahwa error ini ada.
Berikut adalah beberapa cara untuk memeriksa null:
Pemeriksaan
if
val length = if(string != null) string.length else 0
Karena smart-cast dan pemeriksaan null, compiler Kotlin mengetahui bahwa nilai string adalah non-null sehingga memungkinkan Anda menggunakan referensi secara langsung, tanpa memerlukan operator panggilan yang aman.
-
Operator ini memungkinkan Anda menyatakan "jika objek non-null, tampilkan objek; jika tidak, tampilkan objek lain".
val length = string?.length ?: 0
Anda tetap dapat memperoleh NullPointerException
dalam Kotlin. Berikut adalah situasi yang
paling umum:
- Saat Anda secara eksplisit menampilkan
NullPointerException
. - Saat Anda menggunakan
operator
!!
pernyataan null. Operator ini mengonversi semua nilai menjadi jenis non-null, dengan menampilkanNullPointerException
jika nilainya null. - Saat mengakses referensi null jenis platform.
Jenis platform
Jenis platform adalah deklarasi objek yang berasal dari Java. Jenis ini diperlakukan secara khusus; pemeriksaan null tidak diterapkan, sehingga jaminan non-null sama seperti dalam Java. Saat Anda mengakses referensi jenis platform, Kotlin tidak membuat error waktu kompilasi, tetapi referensi ini dapat menyebabkan error runtime. Lihat contoh berikut dari dokumentasi Kotlin:
val list = ArrayList<String>() // non-null (constructor result) list.add("Item")
val size = list.size // non-null (primitive int) val item = list[0] // platform
type inferred (ordinary Java object) item.substring(1) // allowed, may throw an
// exception if item == null
Kotlin mengandalkan inferensi jenis saat nilai platform ditetapkan ke variabel
Kotlin, atau Anda dapat menentukan jenis yang diharapkan. Cara terbaik untuk memastikan status nullability yang benar dari referensi yang berasal dari Java adalah dengan menggunakan anotasi
nullability (misalnya, @Nullable
) dalam kode Java Anda. Compiler Kotlin
akan menampilkan referensi ini sebagai jenis nullable atau non-nullable sebenarnya, bukan sebagai
jenis platform.
Java Jetpack API telah dianotasi dengan @Nullable
atau @NonNull
sesuai kebutuhan,
dan pendekatan serupa telah dilakukan dalam
SDK Android 11.
Jenis yang berasal dari SDK ini, yang digunakan di Kotlin, akan ditampilkan sebagai
jenis nullable atau non-nullable yang benar.
Berkat sistem jenis Kotlin, kami melihat berbagai aplikasi mengalami penurunan error NullPointerException
secara signifikan. Misalnya, aplikasi Google Home mengalami penurunan
error yang disebabkan oleh pengecualian pointer null sebesar 30% pada tahun aplikasi ini
memigrasikan pengembangan fitur baru ke Kotlin.