Latihan Debugging : Latihan - Menemukan, Memperbaiki Error atau Bug, dan Men-Debug Aplikasi - Cara Memperbaiki Error dan Debugging Aplikasi Android.
Tujuan
Ketika mengembangkan aplikasi Android, tentu salah satu hal yang paling menantang adalah saat menemukan bug ketika aplikasi dijalankan. Memecahkan sebuah bug di platform android bukanlah hal yang mudah. Ada beragam spesifikasi perangkat android yang beredar di pasar. Aplikasi kita bisa jadi berjalan lancar di satu peranti. Namun bermasalah di peranti yang berbeda. Memusingkan, bukan? Tetapi inilah tantangannya!
Logika Dasar
Menjalankan aplikasi → muncul bug → menganalisa bug → memperbaiki bug agar aplikasi bisa berjalan dengan baik.
Codelab Debugging
Sangat krusial bagi developer untuk mampu menangani setiap bug yang muncul. Kita perlu mengenal bagaimana cara menemukan bug pada aplikasi Android. Berikut adalah cara-caranya:
Buat Project baru di Android Studio dengan kriteria sebagai berikut:
Nama Project MyTestingApp Target & Minimum Target SDK Phone and Tablet, Api level 21 Tipe Activity Empty Activity Activity Name MainActivity Use AndroidX artifacts True Language Kotlin/Java - Selanjutnya pada activity_main.xml lengkapi kodenya menjadi seperti berikut:
<?xml version="1.0" encoding="utf-8"?>
Tambahkan resource string di dalam res → values → strings.xml.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/btn_set_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text"
android:text="@string/set_value"/>
</RelativeLayout><resources>
<string name="app_name">MyTestingApp</string>
<string name="set_value">Set Nilai</string>
<string name="hello_world">Hello World!</string>
</resources> - Sekarang kita akan melakukan pengujian aplikasi untuk mendapatkan eror NullPointerException. Pada MainActivity silakan lengkapi kodenya menjadi seperti berikut ini:
Kotlin class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var btnSetValue: Button
private lateinit var tvText: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvText = findViewById(R.id.tv_text)
btnSetValue.setOnClickListener(this)
}
override fun onClick(view: View) {
if (view.id == R.id.btn_set_value) {
tvText.text = "19"
}
}
}Java public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnSetValue;
private TextView tvText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvText = findViewById(R.id.tv_text);
btnSetValue.setOnClickListener(this);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_set_value) {
tvText.setText("19");
}
}
} - Setelah selesai, silakan jalankan aplikasi. Seharusnya jika kita klik button Set Nilai, aplikasi akan langsung force close seperti di bawah ini.
Perhatikan tablogcat di bagian bawah editor Android Studio yang menampilkan log berwarna merah seperti berikut ini.
Jelasnya seperti ini:2019-01-21 11:46:47.493 13453-13453/com.dicoding.picodiploma.testingapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.dicoding.picodiploma.testingapp, PID: 13453
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.dicoding.picodiploma.testingapp/com.dicoding.picodiploma.testingapp.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2913)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.dicoding.picodiploma.testingapp.MainActivity.onCreate(MainActivity.java:19)
at android.app.Activity.performCreate(Activity.java:7136)
at android.app.Activity.performCreate(Activity.java:7127)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2893)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)Dengan memperhatikan log di atas, kita dapat mengetahui penyebab erornya :
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.dicoding.picodiploma.testingapp.MainActivity.onCreate(MainActivity.java:19)Eror NullPointerException muncul karena kita mencoba menekan button yang belum diinisiasi (masih bernilai null).
Solusinya, kita perlu menginisiasi obyek dengan cara berikut:Kotlin btnSetValue = findViewById(R.id.btn_set_value)
Java btnSetValue = findViewById(R.id.btn_set_value);
Kemudian jalankan aplikasi kembali, maka eror sudah tidak muncul lagi.
- Selanjutnya kita coba untuk memunculkan eror lainnya dengan menambahkan beberapa baris kode yang ditebalkan pada MainActivity seperti baris berikut:
Kotlin class MainActivity : AppCompatActivity(), View.OnClickListener {
private lateinit var btnSetValue: Button
private lateinit var tvText: TextView
private var names = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnSetValue = findViewById(R.id.btn_set_value)
tvText = findViewById(R.id.tv_text)
btnSetValue.setOnClickListener(this)
names.add("Narenda Wicaksono")
names.add("Kevin")
names.add("Yoza")
}
override fun onClick(view: View) {
if (view.id == R.id.btn_set_value) {
val name = StringBuilder()
for (i in 0..3) {
name.append(names[i]).append("\n")
}
tvText.text = name.toString()
}
}
}Java public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button btnSetValue;
private TextView tvText;
private ArrayList<String> names;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnSetValue = findViewById(R.id.btn_set_value);
tvText = findViewById(R.id.tv_text);
btnSetValue.setOnClickListener(this);
names = new ArrayList<>();
names.add("Narenda Wicaksono");
names.add("Kevin");
names.add("Yoza");
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_set_value) {
StringBuilder name = new StringBuilder();
for (int i = 0; i <= 3; i++){
name.append(names.get(i)).append("\n");
}
tvText.setText(name.toString());
}
}
} - Ketika kita klik button Set Nilai muncul kembali eror seperti berikut:
Dan perhatikan eror pada android monitor seperti berikut:2019-01-21 11:52:04.028 13739-13739/com.dicoding.picodiploma.testingapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.dicoding.picodiploma.testingapp, PID: 13739
java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
at java.util.ArrayList.get(ArrayList.java:437)
at com.dicoding.picodiploma.testingapp.MainActivity.onClick(MainActivity.java:35)
at android.view.View.performClick(View.java:6597)
at android.view.View.performClickInternal(View.java:6574)
at android.view.View.access$3100(View.java:778)
at android.view.View$PerformClick.run(View.java:25885)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)Kode di atas akan memunculkan IndexOutOfBoundsException. Collection names memiliki 3 item. Karena sebuah collection dimulai dari 0, maka item terakhirnya berada pada indeks ke 2. Ketika kita hendak mengambil data ke-3, maka IndexOutOfBoundsException akan dibangkitkan.
Solusinya adalah dengan mengganti nilai maksimal perulangan dari 3 menjadi 2 berikut pada proses perulangan for. Silakan jalankan kembali dan seharusnya sudah berjalan tanpa eror saat ini.Kotlin for (i in 0..2) {
Java for (int i = 0; i <= 2; i++) {
- Selanjutnya kita coba untuk memunculkan eror lainnya dalam menampilkan image, unduh image pada url ini fronalpstock_big.jpg dan ketika selesai ubah nama berkasnya menjadi fronalpstock_big.jpg. Kemudian copy dan paste berkas tersebut ke direktori drawable di Android Studio.
Pada activity_main.xml silakan tambahkan sebuah imageview sehingga keseluruhan kode kita menjadi seperti berikut:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/btn_set_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text"
android:text="@string/set_value"/>
<ImageView
android:id="@+id/img_preview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/btn_set_value"/>
</RelativeLayout>Dan pada MainActivity silakan tambahkan obyek imageview bernama imgPreview dan atur image yang diunduh ke dalamnya.
Kotlin class MainActivity : AppCompatActivity(), View.OnClickListener {
...
private lateinit var imgPreview: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
...
imgPreview = findViewById(R.id.img_preview)
imgPreview.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.fronalpstock_big))
}
...
}Java public class MainActivity extends AppCompatActivity implements View.OnClickListener {
...
ImageView imgPreview;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
imgPreview = findViewById(R.id.img_preview);
imgPreview.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.fronalpstock_big));
}
...
}Yuk jalankan aplikasinya, Munculah OutOfMemoryException.
Process: com.dicoding.picodiploma.mytestingapp, PID: 16968
java.lang.RuntimeException: Canvas: trying to draw too large(183660312bytes) bitmap.
at android.view.DisplayListCanvas.throwIfCannotDraw(DisplayListCanvas.java:229)
at android.view.RecordingCanvas.drawBitmap(RecordingCanvas.java:98)
at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:545)
at android.widget.ImageView.onDraw(ImageView.java:1360)
at android.view.View.draw(View.java:20207)
at android.view.View.updateDisplayListIfDirty(View.java:19082)
at android.view.View.draw(View.java:19935)
at android.view.ViewGroup.drawChild(ViewGroup.java:4333)
at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4112)
...Seperti telah disampaikan sebelumnya, out of memory dapat terjadi karena ukuran gambar yang dimuat melebihi memori yang tersedia untuk menjalankan aplikasi. Solusinya adalah perkecil ukuran gambar sebelum ditampilkan. Gunakan libraryGlide untuk mengecilkan gambar. Cara untuk menambahkan library tersebut yaitu dengan menambahkan dependency Glide di build.gradle(module:app) seperti berikut:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'androidx.appcompat:appcompat:1.0.2'
testImplementation 'junit:junit:4.12'
implementation "androidx.core:core-ktx:1.0.2"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.github.bumptech.glide:glide:4.9.0'
}Kemudian tekanSync di pojok kanan atas layar.
Ubah kode untuk memuat gambar menjadi seperti berikut :
Library glide dapat Anda periksa pada tautan berikut: https://github.com/bumptech/glide. Glide juga sering digunakan untuk memuat gambar yang berasal dari internet dengan memasukkan tautan gambarnya.Kotlin // imgPreview.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.fronalpstock_big))
Glide.with(this).load(R.drawable.fronalpstock_big).into(imgPreview)Java // imgPreview.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.fronalpstock_big));
Glide.with(this).load(R.drawable.fronalpstock_big).into(imgPreview);
Bedah Kode
bug
Terdapat tiga tipe jenis bug umum yang kerap terjadi pada proses pengembangan aplikasi Android:
Bug yang menyebabkan Force Closed
Kelompok bug ini akan menampilkan dialog force closed. Bug ini dapat terjadi ketika kode kita tidak dapat menangani kondisi tertentu saat aplikasi sedang berjalan:Java Null Pointer Exception, umumnya terjadi ketika kode kita mengharapkan obyek tertentu untuk diproses, namun yang ia peroleh adalah nilai null.
Java Out of Memory Exception, umumnya terjadi karena memori yang digunakan oleh aplikasi melebihi jatah yang disediakan oleh sistem android. Bug ini bisa terjadi misalnya ketika kita memuat gambar dengan ukuran besar tanpa mengecilkannya terlebih dahulu.
- Java Index Out Of Bound, terjadi ketika kita mencoba mengakses data dengan indeks yang berada di luar jangkauan ukuran dari sebuah collection seperti arraylist atau linkedlist. Contoh dari masalah ini dapat Anda lihat pada kode berikut ini:
Anggap preList memiliki 2 item di dalamnya. Masalah akan muncul ketika baris ini dijalankan companyName = presList.get(a + 1);.Kotlin for (a in 0..presList.size) {
val companyName = presList[a + 1]
}Java for (int a = 0; a < presList.size(); a++) {
String companyName = presList.get(a + 1);
}
Pada perulangan kedua, nilai a bernilai 1. Ketika ia dijalankan maka ia akan berusaha mengambil item pada posisi ke 2 (a + 1 = 2, karena a bernilai 1). Tidak ada item pada posisi ke 2. Ini karena collection dimulai dari posisi 0. Sehingga posisi valid terakhir untuk presList adalah 1. - Java Memory Leak, ini adalah bug yang sangat berpengaruh pada performa aplikasi karena berhubungan dengan penggunaan memori peranti pengguna.
Bug yang menyebabkan Application is not Responding
Android akan menganggap sebuah aplikasi sebagai not responding bila proses yang berjalan di main thread tidak selesai dalam waktu 5 detik.Bug yang disebabkan oleh kesalahan logika
Ini adalah bug yang berbahaya. Kesalahan dalam kelompok ini dapat menyebabkan aplikasi berperilaku di luar rancangan kita walaupun aplikasi tidak crash.
Ketiga grup bug atau eror yang terjadi di android umumnya didasari pada beberapa hal.
- Pemahaman tentang proses bisnis yang dirancang.
- Pemahaman tentang komponen aplikasi android.
- Konsep OOP dan pemahaman tentang Java yang digunakan.
- Ketelitian dalam menulis program (termasuk kesalahan algoritma yang ada) hingga pada kurang baiknya dalam melakukan pengujian aplikasi.
Tuliskan rancangan testing terlebih dahulu sebelum melakukan penulisan kode. Kenapa? Ini membuat penulisan kode mengarah pada proses pengujian yang sudah direncanakan berdasarkan rancangan proses yang ditentukan.
Ingat, aplikasi mobile itu sangat mahal dalam proses akuisisi pengguna untuk mengunduh aplikasi. Ketika terdapat eror bug yang tak bisa ditolerir , maka otomatis pengguna akan mencopot aplikasi buatan Anda dari perantinya. Sayang kan?
Tenang, kita akan bahas cara mengatasi eror bug pada modul ini, seperti:
Menemukan bug dari membaca android monitor.
Melakukan perbaikan pada bug.
Melakukan proses debugging aplikasi.
Melakukan otomasi testing dengan tools yang disediakan seperti Espresso untuk ui testing dan JUnit untuk unit testing (pada materi selanjutnya).
Debug Point
Inti untuk menemukan sebuah bug adalah dengan membaca errorlog yang ada pada android monitor dengan seksama. Anda dapat melakukan googling dengan kata kunci yang lebih akurat ketika Anda dapat membaca error log.
Setelah kita mempelajari bagaimana menemukan dan mengenali bug umum yang biasa terjadi, sekarang saatnya kita mempelajari bagaimana melakukan proses debugging. Caranya adalah dengan menekan tombolpada menu bar. Kemudian Anda dapat menambahkan debug point dengan menekan baris kode yang ingin kita lihat prosesnya. Dengan adanya debugpoint ini, Anda dapat memantau proses yang sedang berjalan.
Contohnya adalah sebagai berikut:
Kemudian setelah dijalankan, aplikasi akan berhenti di baris debug point tersebut, dan di bagian tab Debug kita bisa melihat nilai tiap-tiap variabel yang ada di activity itu. Jika ingin melanjutkan proses ke debug point selanjutnya, Anda bisa mengklik tombol resume yang berwarna hijau di sebelah kiri.
Pada modul ini Anda telah belajar beberapa bug umum yang terjadi dan bagaimana menelaah masalahnya. Anda juga telah belajar bagaimana melakukan proses debugging.
Inti dari proses di atas adalah ketelitian dan pemahaman yang menyeluruh tentang proses bisnis, algoritma dan alur aplikasi yang dirancang. Kita tidak bisa menjamin bahwa aplikasi kita akan 100% terbebas dari bug. Namun, tetaplah penting bagi kita meminimalisir eror yang terjadi sebelum meluncurkan aplikasi ke publik.
Kita juga dapat memanfaatkan alat seperti Crashlytics dan Firebase untuk memantau kesalahan ketika aplikasi dijalankan oleh pengguna. Anda dapat mempelajari kedua alat tersebut pada tautan di bawah ini:
Artikel yang bisa membantu Anda ketika mengalami eror di Android Studio:
Source code materi ini bisa Anda unduh di tautan berikut: