Pengantar proses debug

Siapa pun yang menggunakan software kemungkinan besar mengalami bug. Bug adalah error dalam software yang menyebabkan perilaku yang tidak diinginkan, seperti aplikasi error, atau fitur tidak berfungsi seperti yang diharapkan. Semua developer, terlepas dari pengalamannya, mengenal bug saat menulis kode, dan salah satu keterampilan paling penting bagi developer Android adalah mengidentifikasi dan memperbaikinya. Tidak jarang mereka melihat keseluruhan rilis aplikasi yang dikhususkan untuk memperbaiki bug. Sebagai contoh, lihat detail versi Google Maps di bawah:

9d5ec1958683e173.png

Proses perbaikan bug disebut proses debug. Ilmuwan komputer terkenal, Brian Kernighan, pernah berkata bahwa "alat proses debug paling efektif masih dipikirkan dengan cermat, ditambah pernyataan cetak yang ditempatkan dengan penuh pertimbangan". Hal tersebut mungkin benar, tetapi alat proses debug yang lebih canggih pasti dapat membantu Anda menemukan bug dengan lebih cepat dan mudah. Seperti halnya pemrograman, meskipun proses debug adalah keterampilan yang akan Anda bangun seiring waktu, tidak ada kata terlalu dini untuk memahami alat proses debug yang disertakan di Android Studio. Dalam tutorial ini, Anda akan mempelajari debugger terintegrasi Android Studio, cara membaca pelacakan tumpukan, dan cara menyusuri kode menggunakan titik henti sementara.

Prasyarat

  • Anda sudah mengetahui cara membuka project di Android Studio.

Yang akan Anda pelajari

  • Cara memasang debugger ke aplikasi yang sedang berjalan.
  • Cara menemukan informasi penting dalam pelacakan tumpukan.
  • Menggunakan titik henti sementara untuk menjeda aplikasi yang sedang berjalan dan memeriksa kode baris demi baris.

Yang akan Anda butuhkan

  • Komputer yang dilengkapi Android Studio.

Daripada men-debug aplikasi yang besar dan kompleks, kita akan memulai dengan project kosong dan sengaja memperkenalkan beberapa kode yang berisi bug untuk membantu mendemonstrasikan fitur proses debug di Android Studio.

Mulai dengan membuat project Android Studio baru, seperti yang ditunjukkan.

  1. Di layar Select a Project Template, pilih Blank Activity.

a949156bcfbf8a56.png

  1. Beri nama aplikasi Proses debug, pastikan bahasa disetel ke Kotlin dan lainnya tidak berubah.

9863157e10628a87.png

  1. Anda akan disambut dengan project Android Studio baru, yang menampilkan file bernama MainActivity.kt.

e3ab4a557c50b9b0.png

Membuat bug

Tidak banyak proses debug yang dapat dilakukan dalam project kosong. Mari tambahkan beberapa kode yang menyebabkan aplikasi ini error.

Ingat pembelajaran di kelas matematika tentang tidak dapat membagi angka dengan nol? Mari kita lihat apa yang terjadi saat kita mencoba membagi dengan nol dalam kode.

  1. Buka MainActivity.kt dan tambahkan fungsi di bawah. Kode ini dimulai dengan dua angka dan menggunakan repeat untuk mencetak hasil pembagian pembilang dengan penyebut lima kali. Setiap kali kode dalam blok repeat berjalan, nilai denominator akan berkurang satu. Pada iterasi kelima dan terakhir, aplikasi akan mencoba membagi dengan nol.
fun division() {
    val numerator = 60
    var denominator = 4
    repeat(5) {
        println(numerator / denominator)
        denominator--
    }
}
  1. Panggil fungsi division() di onCreate(). Fungsi onCreate() baru akan terlihat seperti berikut.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    division()
}
  1. Jalankan aplikasi. Karena onCreate() akan berjalan segera setelah pertama kali ditampilkan, aplikasi akan langsung error saat diluncurkan. Saat aplikasi mengalami error, informasi berguna akan diberikan untuk membantu Anda menemukan error tersebut.
  2. Buka tab Logcat di bagian bawah Android Studio (mungkin perlu waktu agak lama untuk memuat jika tab dibuka untuk pertama kalinya).

e4d025b0363eaa63.png

  1. Jendela Logcat menampilkan banyak output, sehingga Anda mungkin harus men-scroll sedikit untuk menemukan output lainnya. Guna memastikan bahwa hanya output untuk aplikasi Anda yang ditampilkan, setel menu dropdown di kiri atas ke nama emulator (atau perangkat fisik) dan proses ke aplikasi Anda (com.example.debugging).

5c008135b1804091.png

  1. Telusuri pesan error (Control+F di Windows, Command+F di Mac) dengan mengetik "RuntimeException" di kotak penelusuran, lalu tekan Enter.

9468226e5f4d5729.png

Anatomi pelacakan tumpukan

Blok teks yang menjelaskan pengecualian disebut pelacakan tumpukan. Pelacakan tumpukan menampilkan semua fungsi yang dipanggil, yang mengarah ke pengecualian dan dimulai dari fungsi yang terakhir dipanggil. Output lengkapnya ditampilkan di bawah.

Process: com.example.debugging, PID: 23296
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.debugging/com.example.debugging.MainActivity}: java.lang.ArithmeticException: divide by zero
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.ArithmeticException: divide by zero
        at com.example.debugging.MainActivity.division(MainActivity.kt:17)
        at com.example.debugging.MainActivity.onCreate(MainActivity.kt:10)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Teksnya banyak sekali! Untungnya, Anda biasanya hanya memerlukan beberapa langkah yang mempersempit error yang tepat. Mari mulai dari atas.

  1. java.lang.RuntimeException:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.debugging/com.example.debugging.MainActivity}: java.lang.ArithmeticException: divide by zero

Baris pertama menyatakan bahwa aplikasi tidak bisa memulai aktivitas yang menjadi penyebab aplikasi error. Baris berikutnya memberikan lebih banyak informasi. Secara khusus, alasan aktivitas tidak dapat dimulai adalah karena ArithmeticException. Lebih spesifik lagi, jenis ArithmeticException adalah "divide by zero".

  1. Caused by:
Caused by: java.lang.ArithmeticException: divide by zero
        at com.example.debugging.MainActivity.onCreate(MainActivity.kt:17)

Jika Anda men-scroll ke bawah, ke baris "Caused by", lagi-lagi ditampilkan error "divide by zero". Kali ini, laporan ini juga menunjukkan fungsi yang tepat saat terjadi error (division()), dan nomor baris yang tepat (17).

Tidak satu pun dari hal ini akan menjadi kejutan karena bug sengaja diperkenalkan. Namun, jika Anda perlu menentukan penyebab error yang tidak diketahui, mengetahui jenis pengecualian, nama fungsi, dan nomor baris yang tepat akan memberikan informasi yang sangat berguna.

Pada contoh sebelumnya, Anda telah melihat bagaimana pelacakan tumpukan memberikan informasi yang spesifik tentang error seperti fungsi dan baris kode tempat terjadinya error. Saat menangani bug yang sebenarnya, informasi ini mungkin tidak cukup untuk menentukan perbaikan. Misalnya, meskipun Anda tahu di mana bug diperkenalkan dalam kode Anda (baris 17) dan apa error persisnya (membagi dengan nol), Anda mungkin tidak mengetahui alasan kode Anda membagi angka dengan nol. Dalam hal ini, Anda dapat menambahkan pernyataan println() sebelum pembagian terjadi dalam fungsi division() untuk mencetak nilai denominator yang sedang digunakan sebagai penyebut.

fun division() {
    val numerator = 60
    var denominator = 4
    repeat(5) {
        println(denominator)
        println(numerator / denominator)
        denominator--
    }
}

Tentu saja, jika masalah menjadi lebih rumit, Anda harus terus menambahkan pernyataan println() dan menjalankan ulang aplikasi hingga menemukan hal yang berguna. Hal ini bisa sulit dilacak karena kode Anda menjadi lebih kompleks.

Di sinilah titik henti sementara muncul. Meskipun Brian Kernighan mungkin benar tentang nilai dari pernyataan cetak yang ditempatkan dengan baik, titik henti sementara memiliki fungsi yang serupa, namun lebih seperti menekan tombol jeda pada aplikasi yang sedang berjalan. Anda dapat menyetel titik henti sementara pada hampir setiap baris kode. Saat titik henti sementara tercapai, semua eksekusi berhenti, dan Anda dapat memeriksa nilai variabel atau bahkan menyusuri kode Anda dengan hanya mengeksekusi satu baris pada satu waktu. Untuk menggunakan titik henti sementara, Anda perlu menjalankan aplikasi menggunakan sesuatu yang disebut debugger.

Memasang debugger

Di balik layar, Android Studio menggunakan alat bernama Android Debug Bridge (atau disingkat ADB). Ini adalah alat command line yang terintegrasi ke dalam Android Studio dan menyediakan kemampuan proses debug, seperti titik henti sementara, untuk aplikasi yang sedang berjalan. Alat untuk proses debug sering disebut debugger.

Untuk menggunakan (atau memasang) debugger ke aplikasi, Anda tidak dapat langsung menjalankan aplikasi dengan Run > Run seperti sebelumnya. Sebagai gantinya, Anda dapat menjalankan aplikasi dengan Run > Debug 'app'.

21d706a854ebe710.png

Menambahkan titik henti sementara ke project

Lakukan langkah-langkah berikut untuk melihat titik henti sementara berfungsi.

  1. Tambahkan titik henti sementara dengan mengklik spasi kosong di samping nomor baris tempat Anda ingin menjeda. Titik akan muncul di samping nomor baris dan baris akan ditandai.

6b6c2cd97bdc08ba.png

  1. Jalankan aplikasi dengan memasang debugger menggunakan Run > Debug 'app' atau ikon f6a141c7f2a4e444.png di toolbar. Saat aplikasi diluncurkan, Anda akan melihat layar seperti ini.

3bd9cbe69d5a0d0e.png

Setelah aplikasi diluncurkan, Anda akan melihat titik henti sementara yang ditandai saat diaktifkan.

a4860e59534f216a.png

Di bagian bawah layar tempat Anda sebelumnya melihat jendela Logcat, tab Debug baru telah terbuka.

ce37d2791db7302.png

Di sebelah kiri adalah daftar fungsi (sama dengan daftar yang muncul di pelacakan tumpukan), dan di sisi kanan adalah panel tempat Anda dapat memeriksa nilai setiap variabel. Ada juga beberapa tombol di bagian atas yang akan membantu Anda membuka program saat dijeda. Yang paling sering Anda gunakan adalah Step Over yang mengeksekusi satu baris kode yang ditandai.

a6c07c89e81abdc5.png

Lakukan langkah-langkah berikut untuk men-debug kode.

  1. Setelah titik henti sementara tercapai, baris 14 (mendeklarasikan variabel numerator) sekarang ditandai tetapi belum dieksekusi. Gunakan tombol Step Over 1d02d8134802ee64.png untuk mengeksekusi baris 14. Baris 15 sekarang akan ditandai.

58f4bb135d5b756e.png

  1. Tetapkan titik henti sementara pada baris 17. Inilah tempat terjadinya pembagian dan merupakan baris pengecualian yang dilaporkan oleh pelacakan tumpukan.

88d7d810a29965aa.png

  1. Gunakan tombol Resume Program 8119afebc5492126.png di sebelah kiri jendela Debug untuk membuka titik henti sementara berikutnya dan menjalankan fungsi division() lainnya.

433d1c2a610b7945.png

  1. Perhatikan bahwa eksekusi akan berhenti di baris 17 sebelum mengeksekusinya.

1f6aedcf2a48c492.png

  1. Nilai setiap variabel (numerator dan denominator) ditampilkan di samping deklarasi variabel tersebut). Perhatikan bahwa nilai variabel juga dapat dilihat di jendela debug di tab Variables.

ebac20924bafbea5.png

  1. Tekan tombol Resume Program di sebelah kiri jendela debug 4 kali lagi, dengan menjeda setiap kali akan mengobservasi nilai numerator dan denominator. Pada iterasi terakhir, numerator harus 60, dan denominator harus 0. Anda tidak dapat membagi 60 dengan 0.

246dd310b7fb54fe.png

Anda sekarang tidak hanya mengetahui baris kode secara tepat yang menyebabkan bug; Anda juga tahu persis alasannya. Pada iterasi kelima, nilai denominator adalah 0. Anda dapat memperbaiki error ini dengan menambahkan pernyataan if yang akan menjalankan pembagian hanya jika denominator TIDAK sama dengan 0.

fun division() {
    val numerator = 60
    var denominator = 4
    repeat(5) {
        if (denominator != 0) {
            println(numerator / denominator)
        }
        denominator--
    }
}

Anda juga dapat mengubah frekuensi untuk mengulang kode dari 5 menjadi 4.

fun division() {
    val numerator = 60
    var denominator = 4
    repeat(5) {
        println(numerator / denominator)
        denominator--
    }
}

Salah satu dari pendekatan ini akan memungkinkan Anda menjalankan kode tanpa menyebabkan pengecualian runtime. Cobalah dan lihat!

Pada tutorial sebelumnya, Anda menggunakan pernyataan println() Kotlin untuk menghasilkan output teks. Di aplikasi Android, praktik terbaik untuk logging output adalah menggunakan Log. Ada beberapa fungsi untuk logging output, dengan mengambil bentuk Log.e() atau Log.d(), dan mengambil dua parameter: yang pertama, disebut "tag", adalah string yang mengidentifikasi sumber pesan log (seperti nama class yang mencatat log teks). Yang kedua adalah pesan log yang sebenarnya.

Alasan adanya fungsi log yang berbeda, yang diberi nama dengan huruf yang berbeda, adalah karena fungsi tersebut sesuai dengan level log yang berbeda. Bergantung pada jenis informasi yang ingin dihasilkan, Anda akan menggunakan level log yang berbeda untuk membantu memfilternya dalam output Logcat. Ada lima level log utama yang akan Anda gunakan secara rutin.

Level log

Kasus penggunaan

Contoh

ERROR

Log ERROR melaporkan bahwa ada masalah yang sangat serius, seperti alasan aplikasi mengalami error.

Log.e(TAG, "The cake was left in the oven for too long and burned.").

WARN

Log WARN lebih ringan daripada error, tetapi tetap melaporkan sesuatu yang harus diperbaiki untuk menghindari error yang lebih serius. Contohnya mungkin jika Anda memanggil fungsi yang tidak digunakan lagi, yang berarti penggunaannya tidak disarankan untuk alternatif yang lebih baru.

Log.w(TAG, "This oven does not heat evenly. You may want to turn the cake around halfway through to promote even browning.")

INFO

Log INFO memberikan informasi yang berguna, seperti operasi yang berhasil diselesaikan. Ini adalah level log dari pernyataan println().

Log.i(TAG, "The cake is ready to be served.").println("The cake has cooled.")

DEBUG

Log DEBUG berisi informasi yang mungkin berguna saat menyelidiki masalah.

Log.d(TAG, "Cake was removed from the oven after 55 minutes. Recipe calls for the cake to be removed after 50 - 60 minutes.")

VERBOSE

Seperti namanya, ini adalah log panjang. Meskipun yang dianggap sebagai log debug sedikit subjektif dibandingkan dengan log panjang, umumnya log panjang bisa dihapus setelah fitur diimplementasikan, sedangkan log debug mungkin masih berguna untuk proses debug.

Log.v(TAG, "Put the mixing bowl on the counter.")Log.v(TAG, "Grabbed the eggs from the refrigerator.")Log.v(TAG, "Plugged in the stand mixer.")

Perlu diingat bahwa tidak ada aturan yang ditetapkan untuk waktu penggunaan setiap jenis level log, terutama waktu penggunaan INFO, DEBUG, dan VERBOSE. Tim pengembangan software dapat membuat panduannya sendiri tentang kapan harus menggunakan setiap level log, atau mungkin memutuskan untuk sama sekali tidak menggunakan level log tertentu, seperti VERBOSE.

Mari kita lihat seperti apa tampilan level log yang berbeda ini di Logcat.

  1. Di MainActivity.kt, sebelum deklarasi class, tambahkan konstanta yang disebut TAG, dan setel nilainya ke nama class, MainActivity.
private const val TAG = "MainActivity"
  1. Tambahkan fungsi baru ke class MainActivity, yang disebut logging(), seperti yang ditunjukkan.
fun logging() {
    Log.e(TAG, "ERROR: a serious error like an app crash")
    Log.w(TAG, "WARN: warns about the potential for serious errors")
    Log.i(TAG, "INFO: reporting technical information, such as an operation succeeding")
    Log.d(TAG, "DEBUG: reporting technical information useful for debugging")
    Log.v(TAG, "VERBOSE: more verbose than DEBUG logs")
}
  1. Ganti panggilan ke division() (dari contoh sebelumnya) di onCreate() dengan panggilan ke logging(). Metode onCreate() yang baru akan terlihat seperti berikut.
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    logging()
}
  1. Jalankan aplikasi dan observasi output di Logcat. Jika perlu, filter output untuk hanya menampilkan log dari proses com.example.debugging. Anda juga dapat memfilter output untuk hanya menampilkan log dengan tag "MainActivity". Untuk melakukannya, pilih "Edit Filter Configuration" dari menu dropdown di kanan atas jendela Logcat.

5fa189e6b18a966a.png

  1. Lalu ketik "MainActivity" untuk Tag Log, lalu buat nama untuk filter Anda seperti yang ditampilkan.

6dbba17eb5df15eb.png

  1. Sekarang Anda hanya akan melihat pesan log dengan tag "MainActivity".

4061ca006b1d278c.png

Perhatikan bahwa ada huruf sebelum nama class (misalnya W/MainActivity) yang sesuai dengan level log. Selain itu, log WARN ditampilkan dalam warna biru, sementara log ERROR ditampilkan dalam warna merah, seperti error fatal dalam contoh sebelumnya.

  1. Sama seperti cara memfilter output debug berdasarkan proses, Anda juga dapat memfilter output menurut level log. Secara default, nilai ini disetel ke Verbose yang akan menampilkan log VERBOSE dan level log yang lebih tinggi. Pilih Warn dari menu dropdown dan lihat, sekarang hanya log level WARN dan ERROR yang ditampilkan.

c4aa479a8dd9d4ca.png

  1. Ubah lagi menu dropdown ke Assert, dan amati bahwa tidak ada log yang ditampilkan. Ini akan memfilter semua yang berada di level ERROR dan di bawahnya.

169a0bc232f77734.png

Meskipun mungkin terkesan terlalu serius dalam menerima pernyataan println() saat Anda membuat aplikasi yang lebih besar, akan ada banyak output Logcat, dan penggunaan level log yang berbeda akan memungkinkan Anda memilih informasi yang paling berguna dan relevan. Menggunakan Log dianggap sebagai praktik terbaik dan lebih disukai daripada println() dalam pengembangan Android. Memilih level log yang tepat akan menguntungkan orang lain di tim pengembangan Anda yang mungkin belum terlalu memahami kode tersebut, dan jauh lebih mudah untuk mengidentifikasi serta menyelesaikan bug.

Secara default, template Blank Activity yang Anda gunakan untuk membuat project akan menambahkan satu aktivitas, dengan TextView yang berpusat di layar. Seperti yang telah dipelajari sebelumnya, Anda dapat mereferensikan tampilan dari kode dengan menetapkan ID di editor tata letak dan mengakses tampilan dengan findViewByID(). Mari kita coba mengakses tampilan untuk membantu menggambarkan bug lain.

  1. Buka activity_main.xml, pilih Hello, world! TextView dan setel ID ke hello_world.

8a5dede436e2718e.png

  1. Lalu, kembali ke ActivityMain.kt di onCreate(), tambahkan kode untuk mendapatkan TextView dan ubah teksnya menjadi "Hello, debugging!" sebelum panggilan ke setContentView().
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val helloTextView: TextView = findViewById(R.id.hello_world)
    helloTextView.text = "Hello, debugging!"
    setContentView(R.layout.activity_main)
    division()
}
  1. Jalankan lagi aplikasi, dan amati bahwa aplikasi langsung error lagi saat diluncurkan. Filter log berdasarkan com.example.debugging.

cdb335255d798a0a.png

Pengecualian harus berupa salah satu hal terakhir yang muncul di Logcat (jika tidak, Anda dapat menelusuri RuntimeException). Outputnya akan terlihat seperti berikut.

com.example.debugging E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.debugging, PID: 5516
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.debugging/com.example.debugging.MainActivity}: java.lang.IllegalStateException: findViewById(R.id.hello_world) must not be null
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
     Caused by: java.lang.IllegalStateException: findViewById(R.id.hello_world) must not be null
        at com.example.debugging.MainActivity.onCreate(MainActivity.kt:10)
        at android.app.Activity.performCreate(Activity.java:8000)
        at android.app.Activity.performCreate(Activity.java:7984)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7656)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Seperti sebelumnya, di bagian atas akan tertera "Unable to start activity". Hal ini masuk akal karena aplikasi mengalami error sebelum MainActivity diluncurkan. Baris berikutnya memberitahukan error lebih lanjut.

java.lang.IllegalStateException: findViewById(R.id.hello_world) must not be null

Di bagian bawah pelacakan tumpukan, Anda juga akan melihat baris ini, yang menunjukkan panggilan fungsi dan nomor baris yang tepat.

Caused by: java.lang.IllegalStateException: findViewById(R.id.hello_world) must not be null
        at com.example.debugging.MainActivity.onCreate(MainActivity.kt:10)

Apa sebenarnya arti error ini, dan apa sebenarnya maksud nilai "null"? Meskipun ini adalah contoh rumit dan Anda mungkin sudah tahu mengapa aplikasi error, Anda pasti akan menemukan pesan error yang belum pernah dilihat sebelumnya. Saat ini terjadi, Anda bukanlah orang pertama yang melihat error tersebut, dan developer yang paling berpengalaman pun sering kali akan melihat pesan error tersebut untuk mengetahui cara orang lain menyelesaikan masalah tersebut. Pencarian error ini menampilkan beberapa hasil dari StackOverflow, situs tempat developer dapat mengajukan pertanyaan dan memberikan jawaban tentang kode yang berisi bug atau topik pemrograman yang lebih umum.

efa074b344d1704c.png

Jika Anda membaca beberapa jawaban, Anda akan melihat bahwa error tersebut dapat disebabkan oleh berbagai penyebab. Namun, jika membandingkan contoh kami dengan kode di unit 1, Anda akan melihat bahwa kami sengaja mencoba mengakses tampilan sebelum panggilan ke setContentView(). Mencoba mengakses tampilan sebelum tampilan tersebut tersedia akan menyebabkan pengecualian runtime.

Perbaiki error dengan memperbarui kode.

  1. Pindahkan panggilan ke findViewById() dan baris yang menyetel teks helloTextView di bawah panggilan ke setContentView(). Metode onCreate() baru akan terlihat seperti berikut.
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
   val helloTextView: TextView = findViewById(R.id.hello_world)
   helloTextView.text = "Hello, debugging!"
   division()
}
  1. Kemudian, jalankan kembali aplikasi dan amati bahwa kode tidak lagi error, dan bahwa teks diperbarui seperti yang diharapkan.

e52adf1c7bf6f792.png

Rangkuman:

  • Proses debug adalah proses memecahkan masalah bug dalam kode Anda.
  • Pelacakan tumpukan menyediakan informasi tentang pengecualian, seperti fungsi yang sama persis yang menyebabkannya dan nomor baris tempat pengecualian terjadi.
  • Anda bisa menyetel titik henti sementara untuk menjeda eksekusi aplikasi.
  • Saat eksekusi dijeda, Anda dapat "step over" (melangkah ke bagian berikutnya) untuk mengeksekusi satu baris kode saja.

Mempelajari lebih lanjut