QA kami telah mendeteksi bug: ketika memutar perangkat Android (Droid Turbo), kecelakaan terkait RecyclerView berikut terjadi:
java.lang.IndexOutOfBoundsException: Ketidakkonsistenan terdeteksi. Posisi item 2 tidak valid (offset: 2) .state: 3
Bagi saya, sepertinya ada kesalahan internal di dalam RecyclerView, karena saya tidak bisa memikirkan cara apa pun yang disebabkan langsung oleh kode kami ...
Pernahkah seseorang menjumpai masalah ini?
Apa solusinya?
Solusi brutal mungkin bisa untuk menangkap pengecualian ketika itu terjadi dan membuat kembali instance RecyclverView dari awal, untuk menghindari dibiarkan dengan keadaan rusak.
Tetapi, jika memungkinkan, saya ingin memahami masalahnya dengan lebih baik (dan mungkin memperbaikinya pada sumbernya), daripada menutupinya.
Bugnya tidak mudah direproduksi, tetapi fatal saat terjadi.
Jejak tumpukan penuh:
W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
E/AndroidRuntime( 7546): FATAL EXCEPTION: main
E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
E/AndroidRuntime( 7546): at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946)
E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
E/AndroidRuntime( 7546): at andro
Jawaban:
Saya memiliki (mungkin) masalah terkait - memasuki contoh baru dari suatu kegiatan dengan RecyclerView, tetapi dengan adaptor yang lebih kecil memicu kerusakan ini untuk saya.
RecyclerView.dispatchLayout()
dapat mencoba menarik item dari memo sebelum meneleponmRecycler.clearOldPositions()
. Konsekuensinya adalah bahwa itu menarik item dari kolam umum yang memiliki posisi lebih tinggi dari ukuran adaptor.Untungnya, ini hanya dilakukan jika
PredictiveAnimations
diaktifkan, jadi solusi saya adalah dengan subkelasGridLayoutManager
(LinearLayoutManager
memiliki masalah dan 'perbaikan' yang sama), dan menggantisupportsPredictiveItemAnimations()
untuk mengembalikan false:sumber
Dalam kasus saya (hapus / masukkan data dalam struktur data saya), saya perlu menghapus kumpulan daur ulang dan kemudian memberi tahu kumpulan data berubah!
mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();
sumber
Gunakan
notifyDataSetChanged()
sebagai gantinyanotifyItem...
dalam hal ini.sumber
notifyItem...
dan mengoreksi yang akan mulai berfungsi alih-alih merender ulang semua item.Saya memecahkan ini dengan menunda
mRecycler.setAdapter(itemsAdapter)
sampai setelah menambahkan semua item ke adaptor denganmRecycler.addAll(items)
dan itu berhasil. Tidak tahu mengapa saya melakukan itu sejak awal, itu dari kode perpustakaan yang saya melihat dan melihat garis-garis itu dalam "urutan yang salah", saya cukup yakin ini dia, tolong jika seseorang dapat mengkonfirmasi menjelaskan mengapa itu begitu? Tidak yakin apakah ini jawaban yang valid bahkansumber
swapAdapter(adapter, true)
bukansetAdapter(adapter)
dan itu membantu.Saya memiliki masalah yang sama tetapi tidak persis sama. Dalam kasus saya pada 1 titik saya sedang membersihkan array yang diteruskan ke recyclerview
dan tidak memanggil notifyDataSetChanged, karena saya tidak ingin recyclerview segera menghapus pandangan. Saya mengisi ulang array mObjects di AsyncTask.
sumber
Saya memiliki masalah yang sama dengan recyclerView. Jadi saya hanya memberi tahu adaptor tentang perubahan set data setelah daftar dihapus.
sumber
Saya memiliki masalah yang sama. Itu terjadi ketika saya menggulir cepat dan memanggil API dan memperbarui data. Setelah mencoba semua hal untuk mencegah kerusakan, saya menemukan solusi.
Itu akan berhasil.
sumber
Saya mengubah data untuk
RecyclerView
latar belakangThread
. Saya mendapat hal yang samaException
dengan OP. Saya menambahkan ini setelah mengubah data:Semoga ini bisa membantu
sumber
view.recycler_view.post
, saya menggunakannotifyItemInserted
. Dalam kasus saya ini sudah menjadi utas UI.Kesalahan ini terjadi ketika daftar dalam adaptor dihapus ketika pengguna bergulir yang membuat posisi pemegang item berubah, kehilangan referensi antara daftar dan item pada ui, kesalahan terjadi pada permintaan "notDataSetChanged" berikutnya .
Memperbaiki:
Tinjau metode daftar pembaruan Anda. Jika Anda melakukan sesuatu seperti
Bagaimana cara memperbaiki. Buat objek daftar baru untuk pemrosesan buffer dan tetapkan kembali ke daftar utama setelah itu
Terima kasih kepada Nhan Cao untuk bantuan yang luar biasa ini :)
sumber
Masalah saya hilang setelah saya memodifikasi
Adapter
implementasi saya untuk menggunakan salinan array item alih-alih referensi. ThesetItems()
metode ini disebut setiap kali kita memiliki item baru untuk menunjukkan diRecyclerView
.Dari pada:
Aku melakukannya:
sumber
suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true);
dan ini adalah konstruktor sayapublic CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Saya telah menghadapi situasi yang sama. Dan itu diselesaikan dengan menambahkan kode sebelum Anda menghapus koleksi Anda.
mRecyclerView.getRecycledViewPool().clear();
sumber
Dalam kasus saya, saya memperbarui item dan memanggil
notifyDataSetChanged
utas non-UI. Sebagian besar waktu itu berhasil, tetapi ketika banyak perubahan terjadi dengan cepat, itu akan crash. Sebaliknya, ketika saya melakukannya, pada dasarnyalalu berhenti menabrak.
sumber
Anda hanya perlu menghapus daftar Anda
OnPostExecute()
dan tidak saat melakukannyaPull to Refresh
Saya menemukan bahwa ini terjadi ketika Anda menggulir selama tarikan untuk menyegarkan , karena saya membersihkan daftar sebelum
async task
, hasilnyajava.lang.IndexOutOfBoundsException: Inconsistency detected.
Dengan begitu Anda tidak akan berakhir dengan ketidakkonsistenan
sumber
Hal ini juga dapat dikaitkan dengan pengaturan adaptor beberapa kali secara bersamaan. Saya memiliki metode panggilan balik yang dipicu 5-6 kali pada saat yang sama dan saya sedang mengatur adaptor dalam panggilan balik itu sehingga RecycledViewPool tidak dapat menangani semua data tersebut secara bersamaan. Ini peluang yang besar, tetapi Anda sebaiknya memeriksanya.
sumber
Menggunakan
sebagai gantinya
pada kasus ini.
sumber
Untuk memperbaiki masalah ini, panggil saja notifyDataSetChanged () dengan daftar kosong sebelum memperbarui tampilan daur ulang.
Sebagai contoh
hcpArray.clear (); // Daftar untuk pembaruan tampilan daur ulang
sumber
Dalam kasus saya, saya baru saja menghapus baris dengan
setHasStableIds(true);
sumber
Dalam kasus saya, saya mencoba untuk mengubah konten adaptor saya pada utas latar tetapi disebut notify * pada utas utama / ui.
Itu tidak mungkin! Alasan mengapa pemberitahuan dipaksa ke utas utama adalah bahwa recyclerview ingin Anda mengedit adaptor pendukung pada utas utama, bahkan pada tumpukan panggilan yang sama.
Untuk menyelesaikan masalah, pastikan setiap operasi ke adaptor Anda dan juga setiap pemberitahuan ... panggilan dilakukan di utas ui / utama !
sumber
Saya berlari ke jejak tumpukan jahat ini dengan Komponen Arsitektur Android baru-baru ini. Pada dasarnya, saya memiliki daftar item di ViewModel saya yang diamati oleh Fragmen saya, menggunakan LiveData. Ketika ViewModel memposting nilai baru untuk data, Fragment memperbarui adaptor, meneruskan elemen data baru ini dan memberi tahu adaptor bahwa ada perubahan.
Sayangnya, ketika meneruskan elemen data baru ke adaptor, saya gagal menjelaskan fakta bahwa ViewModel dan Adapter akan menunjuk ke referensi objek yang sama! Artinya, jika saya memperbarui data dan menelepon
postValue()
dari dalam ViewModel, ada jendela yang sangat kecil di mana data dapat diperbarui dan adaptor belum diberitahukan!Perbaikan saya adalah untuk membuat salinan elemen baru ketika diteruskan ke adaptor:
mList = new ArrayList<>(passedList);
Dengan perbaikan super mudah ini Anda dapat memastikan bahwa data adaptor Anda tidak akan berubah hingga tepat sebelum adaptor Anda diberi tahu.
sumber
Ini hanya solusi yang bekerja untuk saya bahkan mencoba banyak dari solusi di atas.
1.) Intilisasi
2.) Tulis metode ini dalam adaptor
stockListModels -> daftar ini yang Anda gunakan dalam adaptor.
sumber
Bagi saya, ini berhasil setelah menambahkan baris kode ini:
sumber
masalah ini dapat terjadi ketika Anda mencoba membersihkan daftar Anda, jika Anda akan menghapus daftar data Anda terutama ketika Anda menggunakan tarik untuk menyegarkan mencoba menggunakan bendera boolean, inisialisasi itu sebagai false dan di dalam metode OnRefresh membuatnya benar, bersihkan dataList Anda jika flag benar sebelum menambahkan data baru ke dalamnya dan setelah itu buatlah false.
kode Anda mungkin seperti ini
sumber
Saya memiliki masalah yang sama sebelumnya. Akhirnya menemukan solusi untuk itu
Apa yang saya lakukan adalah memberi tahu adaptor bahwa item telah dihapus dan kemudian memberitahukan kisaran data adaptor berubah
sumber
Saya mengalami masalah serupa dan baru saja menemukan jawabannya. Saya membuat beberapa contoh hard case untuk beberapa test case tetapi tidak memastikan mereka masing-masing mengembalikan ID unik dan itu menyebabkan crash di bawah untuk saya. Memperbaiki ID menyelesaikan masalah, semoga ini membantu orang lain!
sumber
saya pernah mendapat kesalahan juga:
Penyebab: Saya mencoba memperbarui Tampilan Pendaur Ulang dari tugas Async sementara secara bersamaan mencoba untuk mendapatkan viewHolders lama yang dihapus;
Kode: Saya menghasilkan data dengan menekan tombol, logikanya sebagai berikut
Masalah: Setiap kali saya gulir cepat sebelum menghasilkan data saya saya dapatkan
Solusi: daripada menghapus RecyclerView sebelum menghasilkan data saya, saya malah meninggalkannya dan kemudian Ganti dengan Data Baru, Panggilan NotifyDatasetChanged, seperti yang ditunjukkan di bawah ini;
sumber
Hapus saja semua tampilan Manajer tata letak Anda sebelum memberi tahu. Suka:
sumber
Menggunakan
ListAdapter (androidx.recyclerview.widget.ListAdapter)
panggilanadapter.submitList(null)
sebelum meneleponadapter.submitList(list)
:sumber
Pengecualian ini muncul pada API 19, 21 (tetapi bukan yang baru). Di Kotlin coroutine saya memuat data (di utas latar) dan di utas UI ditambahkan dan menunjukkan kepada mereka:
Adaptor:
Untuk beberapa alasan Android tidak membuat cukup cepat atau sesuatu yang lain, jadi, saya memperbarui daftar dengan
post
metodeRecyclerView
(tambah, hapus, perbarui acara item):Pengecualian ini mirip dengan "Tidak dapat memanggil metode ini dalam panggilan balik gulir. Panggilan balik gulir mungkin dijalankan selama pass pengukuran & tata letak di mana Anda tidak dapat mengubah data RecyclerView. Setiap panggilan metode yang mungkin mengubah struktur RecyclerView atau konten adaptor harus ditunda untuk frame berikutnya. ": Recyclerview - tidak dapat memanggil metode ini dalam panggilan balik gulir .
sumber
Saya menemukan pengaturan mRecycler.setLayoutFrozen (true); dalam metode onRefresh dari swipeContainer.
memecahkan masalah untuk saya.
sumber
Ini adalah bug yang tidak menyenangkan.
Untuk menangani klik item saya, saya menggunakan implementasi yang
RecyclerView.OnItemTouchListener
mirip dengan solusi yang ditemukan dalam pertanyaan ini .Setelah berkali-kali menyegarkan
RecyclerView
sumber data dan mengklik suatu item, iniIndexOutOfBoundsException
akan merusak aplikasi saya. Ketika sebuah item diklik, secaraRecyclerView
internal mencari tampilan dasar yang benar dan mengembalikan posisi itu. Memeriksa kode sumber, saya melihat ada beberapaTasks
danThreads
dijadwalkan. Singkatnya, pada dasarnya ini hanya beberapa keadaan ilegal di mana dua sumber data dicampur dan tidak disinkronkan dan semuanya menjadi liar.Berdasarkan hal ini, saya dihapus implementasi saya dari
RecyclerView.OnItemTouchListener
dan hanya tertangkap klik padaViewHolder
dariAdapter
diri sendiri:Ini mungkin bukan solusi terbaik, tetapi bebas crash untuk saat ini .. Semoga ini akan menghemat waktu Anda :).
sumber