可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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
回答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 heigher 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); } }
回答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();
回答3:
Use notifyDataSetChanged()
instead notifyItem...
in this case.
回答4:
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
回答5:
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.
回答6:
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.
回答7:
Use
notifyDataSetChanged()
instead
notifyItemRangeInserted(0, YourArrayList.size())
in this case.
回答8:
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
回答9:
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();
回答10:
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 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
回答11:
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 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() ; } }
回答12:
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.
回答13:
I've faced with the same situation. And it was solved by adding codes before you clear your collection.
mRecyclerView.getRecycledViewPool().clear();
回答14:
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 { private List mItems; (....) void setItems(List items) { mItems = items; } }
I did:
void setItems(List items) { mItems = new ArrayList(items); }
回答15:
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.
回答16:
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!
回答17:
Just remove all views of your layout Manager before notify. like:
myLayoutmanager.removeAllViews();
回答18:
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!
回答19:
I solved the issue, adding items one by one when it gets new data. I use this function inside adapter.
public void add(Data item) { if(!params.contains(item){ params.add(item); notifyItemInserted(getItemCount() - 1); } } }
回答20:
I found that setting mRecycler.setLayoutFrozen(true); in the onRefresh method of the swipeContainer.
solved the problem for me.
swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { orderlistRecycler.setLayoutFrozen(true); loadData(false); } });
回答21:
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 dataList) { if (this.dataList.size() > 0) { notifyItemRangeRemoved(0, dataList.size()); this.dataList.clear(); } this.dataList.addAll(dataList) notifyItemRangeChanged(0, dataList.size()); }
回答22:
This is quite a nasty bug.
To handle my item click, I used an implementation of the RecyclerView.OnItemTouchListener
similar to the solution found in this question.
After many times of refreshing the RecyclerView
's datasource and clicking an item, this IndexOutOfBoundsException
would crash my application. When an item is clicked, the RecyclerView
internally goes looking for the correct underlying view and gives back it position. Checking out the source code, I saw that there were some Tasks
and Threads
scheduled. To cut the story short, basically it's just some illegal state where two datasources are intermixed and not synchronized and the whole thing goes wild.
Based on this, I removed my implementation of the RecyclerView.OnItemTouchListener
and simply caught the click on the ViewHolder
of the Adapter
myself:
public void onBindViewHolder (final BaseContentView holder, final int position) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick (View view) { // do whatever you like here } }); }
This might not be the best solution, but a crash-free on for now.. Hopefully this will save you some time :).
回答23:
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 o) { super.onPostExecute(o); recyclerViewAdapter.setList(o); mProgressBar.setVisibility(View.GONE); mRecyclerView.setVisibility(View.VISIBLE); }
回答24:
In my case I just removed line with setHasStableIds(true);
回答25:
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!!
回答26:
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();