'RecyclerView: Inconsistency detected. Invalid item position
Our QA has detected a bug: when rotating the Android device (Droid Turbo), the following RecyclerView-related crash happened:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
To me, it looks like an internal error inside RecyclerView, as I can't think of any way of this being caused directly by our code...
Has anyone encountered this problem?
What would be the solution?
A brutal workaround could be perhaps to catch the exception when it happens and re-create the RecyclverView instance from scratch, to avoid getting left with a corrupted state.
But, if possible, I would like to understand the problem better (and perhaps fix it at its source), instead of masking it.
The bug is not easy to reproduce, but it is fatal when it happens.
The full stack-trace:
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
Solution 1:[1]
I had a (possibly) related issue - entering a new instance of an activity with a RecyclerView, but with a smaller adapter was triggering this crash for me.
RecyclerView.dispatchLayout()
can try to pull items from the scrap before calling mRecycler.clearOldPositions()
. The consequence being is that it was pulling items from the common pool that had positions higher than the adapter size.
Fortunately, it only does this if PredictiveAnimations
are enabled, so my solution was to subclass GridLayoutManager
(LinearLayoutManager
has the same problem and 'fix'), and override supportsPredictiveItemAnimations()
to return false :
/**
* No Predictive Animations GridLayoutManager
*/
private static class NpaGridLayoutManager extends GridLayoutManager {
/**
* Disable predictive animations. There is a bug in RecyclerView which causes views that
* are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
* adapter size has decreased since the ViewHolder was recycled.
*/
@Override
public boolean supportsPredictiveItemAnimations() {
return false;
}
public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public NpaGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
}
Solution 2:[2]
In my case (delete/insert data in my data structure) I needed to clear recycle pool and then notify data set changed!
mRecyclerView.getRecycledViewPool().clear();
mAdapter.notifyDataSetChanged();
Solution 3:[3]
Use notifyDataSetChanged()
instead notifyItem...
in this case.
Solution 4:[4]
I had the same issue with RecyclerView
So I just notified the adapter about data set change right after the list cleared.
mList.clear();
mAdapter.notifyDataSetChanged();
mList.addAll(newData);
mAdapter.notifyDataSetChanged();
Solution 5:[5]
This error occur when the list in adapter clear when user scrolling which make position of item holder changing, lost ref between list and item on ui, error happen in next "notifyDataSetChanged" request.
Fix:
Review your update list method. If you do something like
mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();
===> Error occur
How to fix. Create new list object for buffer processing and assign again to main list after that
List res = new ArrayList();
…..
res.add(); //add item or modify list
….
mainList = res;
notifyDataSetChanged();
Thanks to Nhan Cao for this great help :)
Solution 6:[6]
I solved this by delaying the mRecycler.setAdapter(itemsAdapter)
till after adding all the items to the adapter with mRecycler.addAll(items)
and it worked. No idea why i did that to begin with, it was from a library's code that I looked over and saw those lines in the "wrong order", I'm pretty sure this is it though, please if someone can confirm it explain why it's so? Not sure if this is a valid answer even
Solution 7:[7]
I had a similar problem but not exactly the same. In my case at 1 point I was clearing the array that was passed to the recyclerview
mObjects.clear();
and not calling notifyDataSetChanged, as I did not want the recyclerview to immediately clear the views. I was re-filling the mObjects array in AsyncTask.
Solution 8:[8]
I have same issue .It was occur when I was scrolling fast and calling API and updating data.After trying all things to prevent crash , I found solution.
mRecyclerView.stopScroll();
It will work.
Solution 9:[9]
I am altering data for the RecyclerView
in the background Thread
. I got the same Exception
as the OP. I added this after changing data:
myRecyclerView.post(new Runnable() {
@Override
public void run() {
myRecyclerAdapter.notifyDataSetChanged();
}
});
Hope it helps
Solution 10:[10]
For me, it worked after adding this line of code:
mRecyclerView.setItemAnimator(null);
Solution 11:[11]
My problem went away after I modified my Adapter
implementation to use a copy of the items array instead of a reference. The setItems()
method is called each time we have new items to show in the RecyclerView
.
Instead of:
private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
private List<MyItem> mItems;
(....)
void setItems(List<MyItem> items) {
mItems = items;
}
}
I did:
void setItems(List<MyItem> items) {
mItems = new ArrayList<>(items);
}
Solution 12:[12]
In my case, I was updating the items and calling notifyDataSetChanged
in a non-UI thread. Most of the time it worked, but when a lot of changes happened quickly, it'd crash. When I did, instead, basically
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
changeData();
notifyDataSetChanged();
}
});
then it stopped crashing.
Solution 13:[13]
This is only solution which worked for me even trying many from above solutions.
1.) Intilization
CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);
2.) Write this method in adapter
public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}
stockListModels -> this list is which you are using in adapter .
Solution 14:[14]
I've faced with the same situation. And it was solved by adding codes before you clear your collection.
mRecyclerView.getRecycledViewPool().clear();
Solution 15:[15]
In my case I just removed line with setHasStableIds(true);
Solution 16:[16]
You only need to clear your list on OnPostExecute()
and not while doing Pull to Refresh
// Setup refresh listener which triggers new data loading
swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
AsyncTask<String,Void,String> task = new get_listings();
task.execute(); // clear listing inside onPostExecute
}
});
I discovered that this happens when you scroll during a pull to refresh, since I was clearing the list before the async task
, resulting to java.lang.IndexOutOfBoundsException: Inconsistency detected.
swipeContainer.setRefreshing(false);
//TODO : This is very crucial , You need to clear before populating new items
listings.clear();
That way you won't end with an inconsistency
Solution 17:[17]
Using ListAdapter (androidx.recyclerview.widget.ListAdapter)
call adapter.submitList(null)
before calling adapter.submitList(list)
:
adapter.submitList(null)
adapter.submitList(someDataList)
Solution 18:[18]
extends LinearLayoutManager and catch this error
public class NoCrashLinearLayoutManager extends LinearLayoutManager {
public NoCrashLinearLayoutManager(Context context) {
super(context);
}
public NoCrashLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public NoCrashLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e){
e.printStackTrace();
}
}
}
Solution 19:[19]
It also can be related with setting the adapter multiple times at the same time. I had a callback method which was triggered 5-6 times at the same time and I was setting the adapter in that callback so RecycledViewPool couldn't handle with all of those datas contemporaneously. It's a fat chance but you better check it out anyway.
Solution 20:[20]
Use
notifyDataSetChanged()
instead
notifyItemRangeInserted(0, YourArrayList.size())
in this case.
Solution 21:[21]
For fix this issue just call notifyDataSetChanged() with empty list before updating recycle view.
For example
//Method for refresh recycle view
if (!hcpArray.isEmpty())
hcpArray.clear();//The list for update recycle view
adapter.notifyDataSetChanged();
Solution 22:[22]
In my case I was trying to change my adapter contents on a background thread but called notify* on the main/ui thread.
That is not possible! The reason why notify is forced to main thread is that the recyclerview wants you to edit your backing adapter on the main thread, even on the same call stack.
To solve the problem make sure that every operation to your adapter as well as every notify... call is made on the ui/main thread!
Solution 23:[23]
I ran into this nasty stack trace with the new Android Architecture Components recently. Essentially, I have a list of items in my ViewModel that are observed by my Fragment, using LiveData. When the ViewModel posts a new value for the data, the Fragment updates the adapter, passing in these new data elements and notifying the adapter that there have been changes.
Unfortunately, when passing in the new data elements to the adapter, I failed to account for the fact that both the ViewModel and the Adapter would be pointing to the same object reference! Meaning that if I update the data and call postValue()
from within the ViewModel, there's a very small window where the data could be updated and the adapter not yet notified!
My fix was to instantiate a fresh copy of the elements when passed in to the adapter:
mList = new ArrayList<>(passedList);
With this super easy fix you can be ensured your adapter data will not change until right before your adapter is notified.
Solution 24:[24]
This exception raised on API 19, 21 (but not new). In Kotlin coroutine I loaded data (in background thread) and in UI thread added and showed them:
adapter.addItem(item)
Adapter:
var list: MutableList<Item> = mutableListOf()
init {
this.setHasStableIds(true)
}
open fun addItem(item: Item) {
list.add(item)
notifyItemInserted(list.lastIndex)
}
For some reason Android doesn't render quick enough or something else, so, I update a list in post
method of the RecyclerView
(add, remove, update events of items):
view.recycler_view.post { adapter.addItem(item) }
This exception is similar to "Cannot call this method in a scroll callback. Scroll callbacks mightbe run during a measure & layout pass where you cannot change theRecyclerView data. Any method call that might change the structureof the RecyclerView or the adapter contents should be postponed tothe next frame.": Recyclerview - cannot call this method in a scroll callback.
Solution 25:[25]
You can reproduce this crash in the following way
- Clear adapter items
list.clear()
. (Do not call notify*** methods) - Scroll recycler view.
So I assume this crash happens when you remove items from the list and scroll without calling notify methods
Solution 26:[26]
this problem may happen when you try clearing your list, if you are going to clear your data list especially when you are using pull to refresh try to use a boolean flag, initialize it as false and inside OnRefresh method make it true, clear your dataList if flag is true just before adding the new data to it and after that make it false.
your code might be like this
private boolean pullToRefreshFlag = false ;
private ArrayList<your object> dataList ;
private Adapter adapter ;
public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{
private void requestUpdateList() {
if (pullToRefresh) {
dataList.clear
pullToRefreshFlag = false;
}
dataList.addAll(your data);
adapter.notifyDataSetChanged;
@Override
OnRefresh() {
PullToRefreshFlag = true
reqUpdateList() ;
}
}
Solution 27:[27]
I had a same issue previously. Finally found a workaround for that
What i do is to notify adapter that item has removed and then notify adapter data set range changed
public void setData(List<Data> dataList) {
if (this.dataList.size() > 0) {
notifyItemRangeRemoved(0, dataList.size());
this.dataList.clear();
}
this.dataList.addAll(dataList)
notifyItemRangeChanged(0, dataList.size());
}
Solution 28:[28]
I ran into a similar issue and just figured it out. I hard-coded a few examples for a test case but didn't ensure they each returned a unique ID and that caused the below crash for me. Fixing the IDs resolved the issue, hope this helps someone else!
Solution 29:[29]
i once got the error too:
Cause: I was trying to update a Recycler View from an Async task while simultaneously trying to get old deleted viewHolders;
Code: I generate data at the press of a button, logic as follows
- Clear the last items in the recycler view
- Call async task to generate data
- OnPostExecute Update the Recycler view and NotifyDataSetChanged
Problem: Whenever i scroll fast before generating my data i get
Inconsistency detected. Invalid view holder adapter positionViewHolder java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 20(offset:2).state:3
Solution: instead of clearing the RecyclerView before generating my data, i instead leave it and then Replace it with the New Data, the Call NotifyDatasetChanged, as shown below;
@Override
protected void onPostExecute(List<Objects> o) {
super.onPostExecute(o);
recyclerViewAdapter.setList(o);
mProgressBar.setVisibility(View.GONE);
mRecyclerView.setVisibility(View.VISIBLE);
}
Solution 30:[30]
Lint gave me an advice concerning inconsistency: I wrote (onBindViewHolder()):
pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doStuff(position);
}
});
which had to be replaced by :
pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doStuff(pholder.getAdapterPosition());
}
});
Run both codes in your code and then run Lint for the full explaination!!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow