RecyclerView: Inconsistency detected. Invalid item position

匿名 (未验证) 提交于 2019-12-03 01:33:01

问题:

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

  1. Clear the last items in the recycler view
  2. Call async task to generate data
  3. 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(); 


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!