Memperbaiki crash “IllegalArgumentException x is unknown to this NavController”
Diterjemahkan dari Resolving crash “IllegalArgumentException x is unknown to this NavController”
Navigation library yang dirilis Google lewat AndroidX tidak perlu diragukan lagi sebagai library yang sangat bermanfaat, meskipun masih memiliki beberapa kekurangan. Contohnya, pada stack trace yang baru-brau ini muncul di Crashlytics crash logs saya:
Fatal Exception: java.lang.IllegalArgumentException
navigation destination com.example.myapp:id/myActionId is unknown to this NavController
Saya tidak dapat menebak kenapa hal ini terjadi karena log yang lain terlihat normal. Usut punya usut, ternyata saat melakukan perpindahan fragment menggunakan myNavController.navigate(R.id.myFragment, bundle)
, crash terjadi bila ada dua event navigasi yang terjadi hampir bersamaan. Biasanya karena kita memiliki daftar link dan user secara tidak sengajak menyentuh dua link sekaligus.
Sentuhan tersebut membuat navigation controller segera mengubah lokasi user di dalam aplikasi. Artinya, navigasi kedua yang juga terklik pada saat yang sama tidak dapat menemukan action yang ingin dilakukan (karena sudah pindah fragment duluan). Contoh, jika sebuah fragment memiliki action untuk pindah ke SubFragmentA dan SubFragmentB, menyentuh kedua link secara bersamaan akan mengakibatkan:
- NavController menerima event SubFragmentA saat masih di Fragment1.
- NavController mencari event yang dicari pad Fragment1 dan menemukannya.
- Lalu, karena NavController sudah mengetahui bahwa tujuan barunya ada SubFragmentA dia mulai melakukan transisi.
- Berikutnya, NavController menerima event navigasi menuju SubFragmentB saat sudah pindah ke SubFragmentA.
- Ketika NavController mencaoba mencari tujuannya yaitu SubFragmentA dari SubFragmentB, ia gagal menemukan jalurnya.
- Crash!
Ada dua solusi. Solusi pertama lebih saya sukai meskipun tidak selalu lebih baik.
Memeriksa lokasi saat ini
Solusi yang pertama dilakukan dengan memeriksa apakah lokasi yang saat ini sedang aktif merupakan lokasi yang memiliki action terpanggil. Cara ini akan berhenti sebelum #4 pada proses di atas. Kekurangannya, kita tidak bisa me-reuse kode navigation dengan mudah di fragment lain karena harus tahu posisi yang akan diperiksa. Tapi, untuk aplikasi yang tidak terlalu besar, langkah ini dapat menyelesaikan masalah dengan mudah:
if (it.findNavController().currentDestination?.id == R.id.fragment_dashboard) {
it.findNavController().navigate(R.id.fragment_detail)
}
Catat bahwa R.id.fragment_dashboard
adalah ID nav graph fragment saat ini, dan R.id.fragment_detail
adalah ID nav graph fragment tujuan.
Catching exception
Solusi yang kedua ini kurang menarik karena hanya melakukan catching pada exception yang dikeluarkan. Solusi ini memungkinkan navigation pertama bekerja dengan baik sehingga mengabaikan navigation kedua.
try {
it.findNavController().navigate(R.id.toDetails)
} catch (e: IllegalArgumentException) {
// User tried tapping 2 links at once!
Timber.e("Can't open 2 links at once!")
}