news

Senin, 15 Juni 2020

Cara Memperbaiki Error dan Debugging Aplikasi Android


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:

  1. 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


  2. Selanjutnya pada activity_main.xml lengkapi kodenya 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"/>

    </RelativeLayout>
    Tambahkan resource string di dalam res → values → strings.xml.
    <resources>
    <string name="app_name">MyTestingApp</string>
    <string name="set_value">Set Nilai</string>
    <string name="hello_world">Hello World!</string>

    </resources>

  3. 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");
    }
    }

    }
  4. Setelah selesai, silakan jalankan aplikasi. Seharusnya jika kita klik button Set Nilai, aplikasi akan langsung force close seperti di bawah ini.
    20190121114730f83e9e49f6132d194b4baedfb60ec061.gif

    Perhatikan tablogcat di bagian bawah editor Android Studio yang menampilkan log berwarna merah seperti berikut ini.
    2019012111482068d28ece70f521d197bd20ef5819f4dc
    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.

  5. 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());
    }

    }
    }
  6. Ketika kita klik button Set Nilai muncul kembali eror seperti berikut:
    20190121115333b4549bbbdb1b05cb2d674fa8e06dcde4.gifDan 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++) {
    20190121115551cb5375d3c546bf0ad7acfd4f33025a8e.gif
  7. 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.
    Screen Shot 2016-12-25 at 12.46.03 PM.png
  8. 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>

  9. 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.
    20190906110541ca868fc20988cd6ded8a5ae3a0bf43eeUbah kode untuk memuat gambar menjadi seperti berikut :

    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);
    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.


Bedah Kode

bug

Terdapat tiga tipe jenis bug umum yang kerap terjadi pada proses pengembangan aplikasi Android:

  1. Bug yang menyebabkan Force Closed
    Android-Force-Close.jpg
    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:
      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);
      }
      Anggap preList memiliki 2 item di dalamnya. Masalah akan muncul ketika baris ini dijalankan 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.

  2. Bug yang menyebabkan Application is not Responding
    zAs7K5STqywU8Gh2T3ku
    Android akan menganggap sebuah aplikasi sebagai not responding bila proses yang berjalan di main thread tidak selesai dalam waktu 5 detik.

  3. 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.

  1. Pemahaman tentang proses bisnis yang dirancang.
  2. Pemahaman tentang komponen aplikasi android.
  3. Konsep OOP dan pemahaman tentang Java yang digunakan.
  4. 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:

  1. Menemukan bug dari membaca android monitor.

  2. Melakukan perbaikan pada bug.

  3. Melakukan proses debugging aplikasi.

  4. 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 tombolScreen Shot 2016-12-25 at 1.06.32 PM.pngpada 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:
20190906111220801504536f976d2f642214b4ae4f0981
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.20190906111623a4a53726386b5dbcb2e5f2667fccc5e7


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: