Saya mengalami masalah dengan komponen Arsitektur Navigasi Android baru saat mencoba menavigasi dari satu Fragmen ke Fragmen lain , saya mendapatkan kesalahan aneh ini:
java.lang.IllegalArgumentException: navigation destination XXX
is unknown to this NavController
Setiap navigasi lainnya berfungsi dengan baik kecuali yang satu ini.
Saya menggunakan findNavController()
fungsi Fragmen untuk mendapatkan akses ke NavController
.
Bantuan apa pun akan dihargai.
java
android
kotlin
android-navigation
android-architecture-navigation
Jerry Okafor
sumber
sumber
Jawaban:
Dalam kasus saya, jika pengguna mengklik tampilan yang sama dua kali dengan sangat cepat, crash ini akan terjadi. Jadi, Anda perlu menerapkan semacam logika untuk mencegah beberapa klik cepat ... Yang sangat mengganggu, tetapi tampaknya perlu.
Anda dapat membaca lebih lanjut tentang mencegah ini di sini: Android Mencegah Klik Dua Kali Tombol
Sunting 19/3/2019 : Hanya untuk memperjelas lebih jauh, kerusakan ini tidak secara eksklusif dapat direkonstruksi hanya dengan "mengklik tampilan yang sama dua kali dengan sangat cepat". Cara lainnya, Anda dapat menggunakan dua jari dan mengeklik dua (atau lebih) tampilan pada saat yang bersamaan, di mana setiap tampilan memiliki navigasi sendiri yang akan mereka lakukan. Ini sangat mudah dilakukan jika Anda memiliki daftar item. Info di atas tentang pencegahan beberapa klik akan menangani kasus ini.
Sunting 16/4/2020 : Seandainya Anda tidak terlalu tertarik untuk membaca postingan Stack Overflow di atas, saya menyertakan solusi (Kotlin) saya sendiri yang telah lama saya gunakan sekarang.
OnSingleClickListener.kt
ViewExt.kt
HomeFragment.kt
sumber
Periksa
currentDestination
sebelum menelepon navigasi mungkin bisa membantu.Misalnya, jika Anda memiliki dua tujuan fragmen pada grafik navigasi
fragmentA
danfragmentB
, dan hanya ada satu tindakan darifragmentA
sampaifragmentB
. meneleponnavigate(R.id.action_fragmentA_to_fragmentB)
akan menghasilkanIllegalArgumentException
saat Anda sudah aktiffragmentB
. Oleh karena itu, Anda harus selalu memeriksacurrentDestination
sebelum melakukan navigasi.sumber
Anda dapat memeriksa tindakan yang diminta di tujuan pengontrol navigasi saat ini.
UPDATE menambahkan penggunaan tindakan global untuk navigasi yang aman.
sumber
currentDestination
daftar tindakan. Katakanlah Anda memiliki tindakan global yang ditentukan dan menggunakan tindakan itu untuk menavigasi. Ini akan gagal karena tindakan tidak ditentukan dalam daftar <action> currentDestination. Menambahkan tanda centang seperticurrentDestination?.getAction(resId) != null || currentDestination?.id != resId
seharusnya menyelesaikannya tetapi juga mungkin tidak mencakup setiap kasus.?: graph.getAction(resId)
->currentDestination?.getAction(resId)
akan mengembalikan tindakan untuk tindakan Global atau non-Global (saya telah mengujinya). Juga, akan lebih baik jika Anda menggunakan Safe Args -> daripada meneruskannavDirections: NavDirections
daripadaresId
danargs
secara terpisah.Ini juga bisa terjadi jika Anda memiliki Fragmen A dengan ViewPager dari Fragmen B Dan Anda mencoba menavigasi dari B ke C
Karena di ViewPager, fragmen bukan tujuan A, grafik Anda tidak akan tahu bahwa Anda berada di B.
Solusinya adalah menggunakan ADirect di B untuk menavigasi ke C
sumber
(parentFragment as? XActionListener)?.Xaction()
dan perhatikan bahwa Anda dapat memegang fungsi ini sebagai variabel lokal jika itu bergunaApa yang saya lakukan untuk mencegah crash adalah sebagai berikut:
Saya memiliki BaseFragment, di sana saya telah menambahkan ini
fun
untuk memastikan bahwadestination
diketahui olehcurrentDestination
:Perlu dicatat bahwa saya menggunakan plugin SafeArgs .
sumber
Dalam kasus saya, saya menggunakan tombol kembali khusus untuk menavigasi ke atas. Saya menelepon
onBackPressed()
sebagai ganti kode berikutHal ini menyebabkan
IllegalArgumentException
terjadinya. Setelah saya mengubahnya untuk menggunakannavigateUp()
metode sebagai gantinya, saya tidak mengalami crash lagi.sumber
TL; DR Tutup
navigate
panggilan Anda dengantry-catch
(cara sederhana), atau pastikan hanya akan ada satu panggilannavigate
dalam waktu singkat. Masalah ini sepertinya tidak akan hilang. Salin cuplikan kode yang lebih besar di aplikasi Anda dan coba.Halo. Berdasarkan beberapa tanggapan bermanfaat di atas, saya ingin membagikan solusi saya yang dapat diperpanjang.
Berikut adalah kode yang menyebabkan kerusakan ini di aplikasi saya:
Cara untuk mereproduksi bug dengan mudah adalah dengan mengetuk dengan banyak jari pada daftar item di mana klik pada setiap item diselesaikan dalam navigasi ke layar baru (pada dasarnya sama dengan yang dicatat orang - dua klik atau lebih dalam waktu yang sangat singkat ). Aku tahu itu:
navigate
Doa pertama selalu bekerja dengan baik;navigate
metode lainnya diselesaikanIllegalArgumentException
.Dari sudut pandang saya, situasi ini mungkin sangat sering muncul. Karena pengulangan kode adalah praktik yang buruk dan selalu baik untuk memiliki satu titik pengaruh, saya memikirkan solusi berikutnya:
}
Dan dengan demikian kode di atas hanya berubah dalam satu baris dari ini:
untuk ini:
Bahkan menjadi sedikit lebih pendek. Kode tersebut diuji di tempat yang tepat di mana kerusakan terjadi. Tidak mengalaminya lagi, dan akan menggunakan solusi yang sama untuk navigasi lain untuk menghindari kesalahan yang sama lebih jauh.
Semua pikiran diterima!
Apa sebenarnya penyebab kecelakaan itu
Ingatlah bahwa di sini kita bekerja dengan grafik navigasi, pengontrol navigasi dan back-stack yang sama ketika kita menggunakan metode
Navigation.findNavController
.Kami selalu mendapatkan pengontrol dan grafik yang sama di sini. When
navigate(R.id.my_next_destination)
dipanggil graph dan back-stack berubah hampir seketika sementara UI belum diperbarui. Hanya saja tidak cukup cepat, tapi tidak apa-apa. Setelah back-stack berubah, sistem navigasi menerimanavigate(R.id.my_next_destination)
panggilan kedua . Karena back-stack telah berubah, kami sekarang beroperasi relatif terhadap fragmen teratas dalam tumpukan. Fragmen teratas adalah fragmen yang AndaR.id.my_next_destination
tuju dengan menggunakan , tetapi tidak berisi tujuan selanjutnya dengan IDR.id.my_next_destination
. Jadi, Anda mendapatkanIllegalArgumentException
karena ID yang tidak diketahui oleh fragmen.Kesalahan persis ini dapat ditemukan dalam
NavController.java
metodefindDestination
.sumber
Dalam kasus saya, masalah terjadi saat saya menggunakan kembali salah satu Fragmen saya di dalam sebuah
viewpager
fragmen sebagai anak dariviewpager
. Theviewpager
Fragment (yang merupakan fragmen induk) ditambahkan dalam xml Navigasi, tetapi tindakan itu tidak ditambahkan dalamviewpager
fragmen induk.Memperbaiki masalah dengan menambahkan tindakan ke fragmen viewpager induk juga seperti yang ditunjukkan di bawah ini:
sumber
Hari ini
Masalahnya masih ada. Pendekatan saya tentang Kotlin adalah:
sumber
Anda dapat memeriksa sebelum menavigasi apakah Fragmen yang meminta navigasi masih merupakan tujuan saat ini, diambil dari inti ini .
Ini pada dasarnya menetapkan tag pada fragmen untuk pencarian nanti.
R.id.tag_navigation_destination_id
hanyalah sebuah id yang harus Anda tambahkan ke ids.xml Anda, untuk memastikannya unik.<item name="tag_navigation_destination_id" type="id" />
Info selengkapnya tentang bug dan solusinya, serta
navigateSafe(...)
metode ekstensi di "Memperbaiki“… yang tidak diketahui oleh NavController ini ”sumber
NAV_DESTINATION_ID
dengan sesuatu seperti ini stackoverflow.com/a/15021758/1572848R.id
.R.id.tag_navigation_destination_id
hanyalah sebuah id yang harus Anda tambahkan ke ids.xml Anda, untuk memastikannya unik.<item name="tag_navigation_destination_id" type="id" />
Dalam kasus saya, saya memiliki beberapa file grafik nav dan saya mencoba untuk berpindah dari 1 lokasi grafik nav ke tujuan di grafik nav lain.
Untuk ini kita harus menyertakan grafik nav ke-2 di yang pertama seperti ini
dan tambahkan ini ke tindakan Anda:
dimana
second_graph
:di grafik kedua.
Info selengkapnya di sini
sumber
Saya telah menyelesaikan masalah yang sama dengan memberi centang sebelum menavigasi alih-alih kode boilerplate untuk mengklik kontrol langsung
menurut jawaban ini
https://stackoverflow.com/a/56168225/7055259
sumber
Dalam kasus saya, bug terjadi karena saya memiliki tindakan navigasi dengan
Single Top
danClear Task
opsi diaktifkan setelah layar splash.sumber
Saya mendapat kesalahan yang sama karena saya menggunakan Panel Samping Navigasi dan
getSupportFragmentManager().beginTransaction().replace( )
pada saat yang sama di suatu tempat dalam kode saya.Saya menghilangkan kesalahan dengan menggunakan kondisi ini (menguji apakah tujuan):
Dalam kasus saya, kesalahan sebelumnya dipicu ketika saya mengklik opsi panel samping navigasi. Pada dasarnya kode di atas memang menyembunyikan kesalahan, karena dalam kode saya di suatu tempat saya menggunakan navigasi menggunakan
getSupportFragmentManager().beginTransaction().replace( )
kondisi -tidak pernah tercapai karena
(Navigation.findNavController(v).getCurrentDestination().getId()
selalu poiting to home fragment. Anda hanya boleh menggunakanNavigation.findNavController(v).navigate(R.id.your_action)
atau nav fungsi pengontrol grafik untuk semua tindakan navigasi Anda.sumber
Sepertinya Anda sedang menyelesaikan tugas. Sebuah aplikasi mungkin memiliki pengaturan satu kali atau serangkaian layar masuk. Layar bersyarat ini tidak boleh dianggap sebagai tujuan awal aplikasi Anda.
https://developer.android.com/topic/libraries/architecture/navigation/navigation-conditional
sumber
Saya menangkap pengecualian ini setelah beberapa kali mengganti nama kelas. Sebagai contoh: Saya memiliki kelas yang dipanggil
FragmentA
dengan@+is/fragment_a
dalam grafik navigasi danFragmentB
dengan@+id/fragment_b
. Lalu saya hapusFragmentA
dan ganti namanyaFragmentB
menjadiFragmentA
. Jadi setelah node yang dariFragmentA
masih tinggal di grafik navigasi, danandroid:name
dariFragmentB
's simpul diubah namanyapath.to.FragmentA
. Saya memiliki dua node dengan yang samaandroid:name
dan berbedaandroid:id
, dan tindakan yang saya butuhkan ditentukan pada node kelas yang dihapus.sumber
Itu terjadi pada saya ketika saya menekan tombol kembali dua kali. Pada awalnya, saya mencegat
KeyListener
dan menimpaKeyEvent.KEYCODE_BACK
. Saya menambahkan kode di bawah ini dalam fungsi bernamaOnResume
untuk Fragmen, dan kemudian pertanyaan / masalah ini terpecahkan.Ketika itu terjadi pada saya untuk kedua kalinya, dan statusnya sama dengan yang pertama, saya menemukan bahwa saya mungkin menggunakan
adsurd
fungsinya. Mari kita analisis situasi ini.Pertama, FragmentA menavigasi ke FragmentB, lalu FragmentB menavigasi ke FragmentA, lalu tekan tombol kembali ... kerusakan muncul.
Kedua, FragmentA menavigasi ke FragmentB, lalu FragmentB menavigasi ke FragmentC, FragmentC menavigasi ke FragmentA, lalu tekan tombol kembali ... kerusakan muncul.
Jadi menurut saya saat menekan tombol back, FragmentA akan kembali ke FragmentB atau FragmentC, lalu menyebabkan login berantakan. Akhirnya saya menemukan bahwa fungsi bernama
popBackStack
dapat digunakan untuk mundur daripada menavigasi.Sejauh ini, masalahnya benar-benar terpecahkan.
sumber
Tampaknya mencampurkan kontrol fragmentManager dari backstack dan kontrol Arsitektur Navigasi dari backstack juga dapat menyebabkan masalah ini.
Misalnya, contoh dasar CameraX asli menggunakan navigasi backstack fragmentManager seperti di bawah ini dan tampak seolah-olah tidak berinteraksi dengan benar dengan Navigasi:
Jika Anda mencatat 'tujuan saat ini' dengan versi ini sebelum berpindah dari fragmen utama (dalam kasus ini, fragmen kamera) dan kemudian mencatatnya lagi saat Anda kembali ke fragmen utama, Anda dapat melihat dari id di log bahwa id tidak sama. Saat menebak, Navigation memperbaruinya saat berpindah ke fragmen dan fragmntManager tidak mengupdatenya lagi saat kembali. Dari log:
Versi terbaru dari contoh dasar CameraX menggunakan Navigasi untuk mengembalikan seperti ini:
Ini bekerja dengan benar dan log menunjukkan id yang sama ketika kembali ke fragmen utama.
Saya menduga moral dari cerita ini, setidaknya saat ini, adalah sangat berhati-hati dalam mencampurkan Navigasi dengan navigasi fragmentManager.
sumber
Cara yang konyol tapi sangat ampuh adalah: Sebut saja ini:
Buat saja Ekstensi ini:
sumber
Mungkin ada banyak alasan untuk masalah ini. Dalam kasus saya, saya menggunakan model MVVM dan saya mengamati boolean untuk navigasi ketika boolean itu benar -> navigasi yang lain jangan lakukan apa-apa dan ini berfungsi dengan baik tetapi ada satu kesalahan di sini
ketika menekan tombol kembali dari fragmen tujuan saya mengalami masalah yang sama. Dan masalahnya adalah objek boolean karena saya lupa mengubah nilai boolean menjadi false ini menciptakan kekacauan. Saya baru saja membuat fungsi di viewModel untuk mengubah nilainya menjadi false dan memanggilnya tepat setelah findNavController ()
sumber
Biasanya ketika ini terjadi pada saya, saya memiliki masalah yang dijelaskan oleh Charles Madere: Dua peristiwa navigasi dipicu pada ui yang sama, satu mengubah currentDestination dan yang lainnya gagal karena currentDestination diubah. Ini dapat terjadi jika Anda mengetuk dua kali, atau mengeklik dua tampilan dengan pendengar klik yang memanggil findNavController.navigate.
Jadi untuk mengatasi ini, Anda dapat menggunakan if-check, try-catch, atau jika Anda tertarik, ada findSafeNavController () yang melakukan pemeriksaan ini untuk Anda sebelum menavigasi. Ini juga memiliki pemeriksaan lint untuk memastikan Anda tidak melupakan masalah ini.
GitHub
Artikel yang merinci masalah
sumber
Jika Anda mengklik terlalu cepat, itu akan menyebabkan null dan crash.
Kami dapat menggunakan RxBinding lib untuk membantu dalam hal ini. Anda dapat menambahkan throttle dan durasi pada klik sebelum itu terjadi.
Ini artikel tentang throttling pada Android kekuatan bantuan. Bersulang!
sumber
Jika Anda menggunakan recyclerview, cukup tambahkan cooldown click listener pada klik Anda dan juga dalam penggunaan file xml recyclerview Anda
android:splitMotionEvents="false"
sumber
Setelah memikirkan saran Danau Ian di utas twitter ini, saya datang dengan pendekatan berikut. Setelah
NavControllerWrapper
didefinisikan seperti itu:Kemudian di kode navigasi:
sumber
Ini terjadi pada saya, masalah saya adalah saya mengklik FAB
tab item fragment
. Saya mencoba menavigasi dari salah satu fragmen item tab keanother fragment
.Tetapi menurut Danau Ian dalam jawaban ini kita harus menggunakan
tablayout
danviewpager
, tidak ada komponen pendukung navigasi . Karenanya, tidak ada jalur navigasi dari tablayout yang berisi fragmen ke item tab.ex:
Solusinya adalah membuat jalur dari tata letak tab yang berisi fragmen ke fragmen yang diinginkan misalnya: jalur:
container fragment -> another fragment
Kerugian:
sumber
Dalam kasus saya, saya mendapat kesalahan itu ketika mencoba menavigasi dari utas lain, dalam 50% kasus. Jalankan kode pada thread utama membantu
sumber
Dalam kasus saya ini terjadi ketika saya tidak sengaja menambahkan
+
tujuan dalam tindakan, dan crash hanya terjadi ketika saya pergi ke fragmen yang sama beberapa kali.Solusinya adalah untuk menghapus
+
dari tujuan tindakan, gunakan hanya@id/profileFragment
sebagai pengganti@+id/profileFragment
sumber
Memperbarui solusi @Alex Nuts
Jika tidak ada tindakan untuk fragmen tertentu dan ingin membuka fragmen
sumber
Saya menulis ekstensi ini
sumber
Saya membuat fungsi ekstensi ini untuk Fragmen:
sumber