[android] RecyclerView: Inconsistency detected. Invalid item position



13 Answers

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();

Question

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



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.




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!




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.




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();



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




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




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);

        }
    });



Sorry For late but perfect solution: when you try to remove a specific item just call notifydatasetchange() and get these item in bindviewholder and remove that item and add again to the last of list and then chek list position if this is last index then remove item. basically the problem is come when you try to remove item from the center. if you remove item from last index then there have no more recycling and also your adpter count are mantine (this is critical point crash come here) and the crash is solved the code snippet below .

 holder.itemLayout.setVisibility( View.GONE );//to hide temprory it show like you have removed item

        Model current = list.get( position );
        list.remove( current );
        list.add( list.size(), current );//add agine to last index
        if(position==list.size()-1){// remove from last index
             list.remove( position );
        }



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 :).




In my case I just removed line with setHasStableIds(true);




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);
}



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!




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




Related