341

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
Reza
  • 906
  • 2
  • 15
  • 29
KarolDepka
  • 8,318
  • 10
  • 45
  • 58
  • 2
    A question: How consistent is your repro? I know this is an error in google's code [here](https://code.google.com/p/android/issues/detail?can=1&q=77846&colspec=ID%20Type%20Status%20Owner%20Summary%20Stars&id=77846) and [here](https://code.google.com/p/android/issues/detail?id=77232). But this can be avoided. So, is this happening on every rotate? – Vic Vuci May 13 '15 at 16:53
  • 1
    Hi. It happens only rarely, but when it happens, it is fatal to the app. – KarolDepka May 13 '15 at 17:12
  • Thanks for the links to the bugs. The first one seems more relevant than the second one. – KarolDepka May 13 '15 at 17:22
  • 1
    Yeah I think your best bet is to just not allow changes to the listview during rotation. – Vic Vuci May 13 '15 at 17:40
  • 1
    If you could reproduce easily I would suggest printing the value for 'getItemCount' before all calls to 'notify*' ... you might discover your item count does not match your assumptions. – Rich Ehmer Jun 17 '15 at 20:21
  • @karolrvn It's also amazing to me that almost every article on RecyclerViews has its focus on creating a list, but none on saving the list. It does no good to create a beautifully crafted/valuable list (say a big list of songs I want to download) and then have the list literally be destroyed if I navigate away from the list with the single press of the back button. Why wouldn't saving state code be a requirement for creating every RecyclerView list just like the other requirements: the adapter, the layoutmanager, onCreateViewHolder, onBindViewHolder and getItemCount? Am I missing something? – AJW Feb 17 '16 at 03:42
  • 3
    Rule to remember is: "always notify...() immediately after changing the dataset, and make sure to call it from the same thread". – Richard Le Mesurier Jul 26 '17 at 12:59
  • @AJW You should focus on "saving the list" when you receive items to display in the list. Your list should use an in-memory copy of already-persisted data as its data source. Taking the attitude of 'saving the list' with the back button is too late - if the app crashes, or battery dies, etc. Look into, for example CursorAdapter. – androidguy Nov 18 '17 at 23:11
  • 3
    If you called `adapter.setHasStableIds()`, just remove this call. It worked for me. – alpha May 21 '18 at 13:01
  • @KarolDepka Did you find the definitive answer? – Alireza Noorali Dec 09 '18 at 08:32
  • Feels like a thread-safety issue – Nathan B Mar 27 '21 at 09:17
  • 1
    @RichardLeMesurier That should be the accepted answer! – cesargastonec Jul 14 '21 at 02:53

51 Answers51

264

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);
    }
}
Bugs Happen
  • 2,169
  • 4
  • 33
  • 59
Kas Hunt
  • 3,098
  • 1
  • 12
  • 15
  • 5
    This worked for me, and disabling predictive animations doesn't cause you to lose the animations all together. Bravo. – Bob Liberatore Dec 03 '15 at 21:39
  • 8
    Thank you very much Sir! Worked instantly with LinearLayoutManager, probably saving me days. – levavare Dec 21 '15 at 10:48
  • 8
    Thanks a lot. This solution is worked with LinearLayoutManager. – Pruthviraj Mar 02 '16 at 05:54
  • 11
    I think this guy deserve we build a statue in honour of his precious help... It's one of the worst documented issue on the web, but it seems lots of dev encounter this issue... I just wonder *how* did you find it could be skipped if PredictiveAnimations are false, @KasHunt ? Because, the stacktrace is very unclear... – PAD Jul 14 '16 at 18:46
  • 3
    Another way to get rid of this is to call notifyDataSetChanged() immediately after setting new adapter on RecyclerView. – s.maks Jul 21 '16 at 13:58
  • 1
    Not working for me! I do not change the Adapter every time new data is set (because the items are added to the existing collection). So I just call notifyItemInserted normally but the exception is still thrown. `notifyDataSetChanged` sounds that it should work but still the same. – King King Oct 19 '16 at 02:00
  • 2
    Is there a corresponding bug ticket on Android Support Library or Google Code? – AAverin Dec 14 '16 at 07:21
  • 1
    Wasted 2 days with this bug. Its almost 3 years since bug was reported but not solution from Google yet. – Rajeev Dec 22 '16 at 07:39
  • It also solves my problem I have a issue with firebase recycler adapter. Whatever manager you can use the main key is this one `@Override public boolean supportsPredictiveItemAnimations() { return false; }` – Subho Feb 05 '17 at 22:04
  • Please attach the link to the bug report in Google's issue tracker. – Egor Apr 20 '17 at 13:02
  • 1
    What will this cause the RecyclerView? Any disadvantages? – android developer Apr 21 '17 at 20:41
  • 1
    I want to believe the solution is this simple. But in RecyclerView.java class RecyclerView.LayoutManager, here is the base impl: `public boolean supportsPredictiveItemAnimations() { return false; }` and it's not overriden in LinearLayoutManager. Am I missing something? Support lib v26. – androidguy Nov 18 '17 at 23:19
  • I know angel... and he have given answer of this question. are you able to explain why really this is happening? – Vaibhav Kadam Jan 05 '18 at 18:20
  • Wasted 3 days with this bug. still now not fixed yet. any other solutions ? – Sagar Jethva Mar 08 '18 at 09:57
  • 8
    Anybody knows, how to fix that without this hack? Because notifyDatasetChanged was dropped in favor of DiffUtil – Anton Shkurenko Mar 21 '18 at 13:02
  • 2
    I added this to my code and it seems to have reduced the number of crashes but they didn't fully go away. Are there other reasons I would get `Inconsistency detected. Invalid item position 184`? – casolorz Oct 26 '18 at 16:33
  • 3
    worked for me with ListAdapter, setting recyclerView.itemAnimator = null – Mihaela Romanca Oct 31 '19 at 13:14
  • This solves same porblem, when populating my adapter when notifying changes ;) – Ric17101 Dec 07 '20 at 05:09
  • I was also facing the same issue while using `PagingDataAdapter` and `GridLayoutManager` and this is the only solution that worked for me. Thanks a lot! – Shahood ul Hassan Aug 19 '22 at 12:44
  • For me I had to add above solution for **LinearLayoutManager** and also **notifyDataSetChanged()** right after when I set Adapter to RecyclerView. Thanks for the solution. – Rav Aug 17 '23 at 05:32
113

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

MatejC
  • 2,167
  • 1
  • 18
  • 25
  • 10
    I don't normally say this, BUT THANK YOU SO MUCH. I have tried EVERYTHING to fix this crash which happens sporadically when I'm moving a bunch of items on the list around in rapid order. I've spent literally probably a full week on trying to solve this issue. I walked away from it for a couple of months to try to give my brain a chance to approach it differently, then found this on my first Google attempt. Bless you! – Chantell Osejo Aug 26 '16 at 20:41
  • why do you have to do this? – dabluck Mar 03 '17 at 15:36
  • 20
    That's a pretty heavy operation that sort of defeats the purpose of recycling views. – gjsalot May 08 '17 at 16:25
  • @gjsalot So if I use this, can it cause some problems? – Sreekanth Karumanaghat Mar 20 '18 at 10:50
  • 2
    Where I have to write this code if I am using MVVM model.? – Nadeem Shaikh Jun 25 '18 at 09:41
33

Use notifyDataSetChanged() instead notifyItem... in this case.

Khai Nguyen
  • 3,065
  • 1
  • 31
  • 24
  • 5
    In some cases this is the way to go. I had a situation where I replaced all my items, but I wasn't being honest with the adapter, only telling it I had inserted some new items (notifyItemRangeInserted), without first telling it I had also removed items. The adapter then expected there to be more items then there actually were. If using any of the adapter's notify methods expect notifyDataSetChanged, such as notifyItemRangeRemoved / Inserted / Updated, the caller has the full responsibility of telling the adapter *exactly* what was changed, or you may end up with this "inconsistent state". – JHH Dec 18 '15 at 10:04
  • 45
    This is not a solution at all. – Miha_x64 Jul 22 '17 at 21:39
  • This is not the way to go. If this works that means you just messed up the range for `notifyItem...` and correcting that will start working instead of re-rendering all the items. – Ranjan Jul 12 '18 at 07:13
  • @Ranjan RecyclerView doesn't "re-rendering all the items", it just renders what is on the screen ("visible") – Farid Feb 03 '22 at 06:29
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();
Reza
  • 906
  • 2
  • 15
  • 29
19

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

Ramesh Pokharel
  • 411
  • 5
  • 15
15

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

Odaym
  • 1,896
  • 1
  • 20
  • 31
  • I think this is the solution, once i delayed the adapter it was fine i believe... now it pops up when set adapter in UI thread and adding items to it. – EngineSense Apr 06 '16 at 16:00
  • 20
    I used `swapAdapter(adapter, true)` instead of `setAdapter(adapter)` and it helped. – frangulyan Jul 30 '16 at 17:58
13

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.

Anand Savjani
  • 2,484
  • 3
  • 26
  • 39
12

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.

Aalap
  • 2,847
  • 2
  • 26
  • 24
7

For me, it worked after adding this line of code:

mRecyclerView.setItemAnimator(null);
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
Fatih Gee
  • 196
  • 2
  • 11
  • 2
    this is not a fix in most cases, if you want animation you have te rewrite your adapter code and find your errors in notifying changes – Dragos Rachieru Mar 21 '19 at 08:20
  • It works fine for me.I'm using real adapter so I can't control the flow and I enabled windowActivityTransitions in a style that what cause this issue thanks man you save my day. – Arul May 02 '20 at 14:59
6

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

  • thanks man! this is the only answer that makes sense from an Android development point of view. – philtz Jan 14 '20 at 00:19
  • While I have also solved with the help of `view.recycler_view.post`, I used `notifyItemInserted`. In my case it has already been UI thread. – CoolMind Feb 26 '20 at 12:18
5

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.

Androiderson
  • 16,865
  • 6
  • 62
  • 72
Erhannis
  • 4,256
  • 4
  • 34
  • 48
4

I've faced with the same situation. And it was solved by adding codes before you clear your collection.

mRecyclerView.getRecycledViewPool().clear();

4

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);
}
  • This will solve the problem, but wont it take up twice the original memory? – Sreekanth Karumanaghat May 05 '18 at 10:32
  • @MiguelA.Gabriel this will effect performance? for example in my case i am updating recylerview's array too frequently so currently i am doing this `suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); ` and this is my constructor `public CandidatesAdapter(List suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }` – Mateen Chaudhry May 17 '18 at 05:50
  • @mateen-chaudhry It probably will. You'll need to test it in your case and decide, or try using another of the proposed solutions. As I said, it is only a workaround and it works for me in my case. – Miguel A. Gabriel May 17 '18 at 06:59
4

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

Jorgesys
  • 124,308
  • 23
  • 334
  • 268
darkchaos
  • 197
  • 1
  • 11
4

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 .

Reza
  • 906
  • 2
  • 15
  • 29
Ramkesh Yadav
  • 987
  • 2
  • 11
  • 16
3

Use

notifyDataSetChanged()

instead

notifyItemRangeInserted(0, YourArrayList.size())

in this case.

Pankaj Talaviya
  • 3,328
  • 28
  • 31
  • 1
    but this is not good for performance right? notifyItemRangeInserted is better, the problem does not lie here – Derekyy Sep 14 '17 at 07:04
3

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

Fahad
  • 1,943
  • 22
  • 27
3

Using ListAdapter (androidx.recyclerview.widget.ListAdapter) call adapter.submitList(null) before calling adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)
Sergio
  • 27,326
  • 8
  • 128
  • 149
  • 1
    I am also using ListAdapter, but if we submitList(null) and then the actual data list then we are not really utilizing the benefits of ListAdapter – Gauri Gadkari May 25 '22 at 22:18
3

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();
        }
    }
}
Rickon Gao
  • 116
  • 6
3

You can reproduce this crash in the following way

  1. Clear adapter items list.clear(). (Do not call notify*** methods)
  2. Scroll recycler view.

So I assume this crash happens when you remove items from the list and scroll without calling notify methods

Shams
  • 388
  • 1
  • 2
  • 13
2

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.

Mustafa Güven
  • 15,526
  • 11
  • 63
  • 83
  • 1
    yes same problem.. But solution? You give the reason only.. How to fix? – Ranjithkumar May 23 '17 at 09:58
  • @RanjithKumar, Kindly share your way to resolve the above issue. I resolved it using mRecyclerView.getRecycledViewPool().clear(); before notifyDataSetChanged and using synchronized block around update function of adapter – Attiq ur Rehman Feb 07 '18 at 08:08
  • can you please take a look at my code i think my problem is like yours can you help me https://stackoverflow.com/questions/50213362/updating-recycler-view-from-handlerthread?noredirect=1#comment87443498_50213362 – Mateen Chaudhry May 09 '18 at 17:23
2

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!

  • 2
    adding items to the list in your adapter should be done on a background thread and call notify on postexecute. adding of data in ui thread makes the app freeze for some millisecond or seconds if adding many data – dione llorera Sep 01 '18 at 15:31
  • agreed with @dionellorera, it should be made clear that a "change in adapter contents" specifically means modifying any data directly, whether primitive values, properties of objects, or objects themselves – OzzyTheGiant Sep 13 '19 at 16:28
2

Just remove all views of your layout Manager before notify. like:

myLayoutmanager.removeAllViews();
BaBaX Ra
  • 251
  • 1
  • 3
  • 8
2

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.

Steve
  • 538
  • 4
  • 17
2

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.

CoolMind
  • 26,736
  • 15
  • 188
  • 224
2

I've just fixed the same issue. I had a RecyclerView.Adapter with setHasStableIds(true) set to avoid items blinking.

I was using a duplicatable field in getItemId() (my model has no id field):

override fun getItemId(position: Int): Long {
    // Error-prone due to possibly duplicate name.
    return contacts[position].name.hashCode().toLong()
}

getItemId() should return a unique id for each item, so the solution was to do it:

override fun getItemId(position: Int): Long {
    // Contact's phone is unique, so I use it instead.
    return contacts[position].phone.hashCode().toLong()
}
Yamashiro Rion
  • 1,778
  • 1
  • 20
  • 31
1

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

}
Moaz H
  • 776
  • 1
  • 6
  • 5
1

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

 }
Cheng
  • 775
  • 2
  • 12
  • 28
1

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!

DocBot
  • 21
  • 2
1

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();
Jorgesys
  • 124,308
  • 23
  • 334
  • 268
EKN
  • 1,886
  • 1
  • 16
  • 29
1

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<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }
EdgeDev
  • 2,376
  • 2
  • 20
  • 37
  • can you please take a look at my code i think my problem is like yours [link] (https://stackoverflow.com/questions/50213362/updating-recycler-view-from-handlerthread?noredirect=1#comment87443498_50213362) – Mateen Chaudhry May 09 '18 at 17:12
1

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!!

1

One possible reason for this crash.

Reloading data while recyclerview is still scrolling.

So, try recyclerview.stopScroll() before reloading your data again.

0

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

        }
    });
Mark Sheekey
  • 608
  • 7
  • 23
0

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

Community
  • 1
  • 1
DroidBender
  • 7,762
  • 4
  • 28
  • 37
  • Creating a new object every time onBind gets called will result in a lot of objects that are going to be garbage collected and the user might experience freezing. – dephinera Aug 15 '18 at 11:18
0

add_location.removeAllViews();

            for (int i=0;i<arrayList.size();i++)
            {
                add_location.addView(new HolderDropoff(AddDropOffActivtity.this,add_location,arrayList,AddDropOffActivtity.this,this));
            }
            add_location.getAdapter().notifyDataSetChanged();
0

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 );
        }
Irfan Ullah
  • 69
  • 2
  • 9
0

Very late response, but this may help someone on the feature.

Make sure that your onStop or onPause methods aren't clearing any of your lists

just aguy
  • 47
  • 8
0

The problem occured on me when I had not recoginized I did call two times with different threads simultaneously .

notifyDataSetChanged

one from sqlite load function one from after calling function

Ucdemir
  • 2,852
  • 2
  • 26
  • 44
0

One way I managed to fix this (in a Kotlin app with architecture components) was by setting if (recyclerView.adapter == null) recyclerView.adapter = MyAdapter(datasource) after I fetched the data from the Repository. Apparently it might have something to do with async issues with the suspend functions in the repository because when I fetch data for the first time when starting the activity, it calls the REST API since there is no data in the db, and everything goes smoothly, but after that, that same query cannot be made again, causing a crash.

OzzyTheGiant
  • 711
  • 8
  • 21
0

In my case I got this exception

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder

None of the answers above are worked in my case. Because I was updating/changing an existing item in the adapter but I used

myAdapter.notifyItemInserted(position)

Instead, I should use this

myAdapter.notifyItemChanged(position)

Note: We should use notifyItemInserted() when we insert an item and we should use notifyItemChanged() when we update an item in the adapter.

Shailendra Madda
  • 20,649
  • 15
  • 100
  • 138
  • That's strange. I also confused both methods one time, but it didn't lead to the exception. It sometimes didn't update a list correctly. – CoolMind Aug 24 '20 at 10:08
0

Most of the answers are disabling animations or creating new copies of data which is an overkill in my opinion. They do work but they don't address the root cause of the issue. In my case it was due to using DiffUtils in a wrong way. In the areItemsTheSame(old, new) method I did not properly implement the equality check, that, in turn cased the adapter to be notified by notifyItemRangeInserted() even though the same number of items are in the new list. So pay close attention to how you implement the DiffUtils callbacks!

abdu
  • 667
  • 5
  • 14
  • How could it be possible? Are you writing in Java (not Kotlin with data-classes)? – CoolMind Aug 24 '20 at 10:06
  • @CoolMind I was using Kotlin data classes and when you have another object as a property in the data class the equality check of data classes will not call equality check recursively – abdu Aug 26 '20 at 08:18
0

If notifyItemChanged(int position), notifyItemInserted(int position) Use instead of each other This problem occurs when you want to update the item use notifyItemChanged and when you want to add the item use notifyItemInserted

emad pirayesh
  • 654
  • 7
  • 12
0

Updated RecyclerView to newest 1.2.1 version. Aaand Magic happend! Couldn't break my app anymore :) So.. try to replace old version number with the newest one in your gradle file

dependencies {
    ...
    // RecyclerView
    def recyclerview_version = "1.2.1"
    implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
}
Andrij
  • 218
  • 2
  • 8
0

If you have more than one recycler views sharing the same adapter, then this error pops-up so to avoid this error create separate adapters for those recycler views. This makes the recycler view to have unshared pools which stops the adapters to populate them when not required.

Tom Shaw
  • 139
  • 12
0

In my case, I'm using DiffUtil.calculateDiff for recycler view items, which has a race condition while updating items. I calculate data diff in background threads pool and update it in UI thread. If 2 data come, and the first data is set to adaptor between the timing that the second diff is calculated and set, the crash happens.

For example, say we have original item list A, new data B, and new data C:

  1. B-diff = calculated (A, B)
  2. C-diff = calculated (A, C)
  3. set(B-diff)
  4. set(C-diff)

The C-diff is actually incorrect since A has been updated, so if the items count decreases, recyclerview would animate on invalid positions. Not just crashes, the items may not be updated at all (or unncessary update) due to incorrect diff index.

Changing to AsyncListDiffer resolved the issue for me since it drops the first data if the second data comes too early.

reference (it describes the issue but gives another solution (updating data in order)): https://jonfhancock.com/get-threading-right-with-diffutil-423378e126d2

汪枭杰
  • 11
  • 1
0

i also had same problem since i was using different layout when dataset was empty:-

while adding first item try

adapter.notifyDataSetChanged()

then continue with

adapter.notifyItemChangedAtPosition(position)
shubham singh
  • 79
  • 1
  • 2
0

If you are using setHasStableIds(true) you must be sure all dataset items has different ids, otherwise that error may be occur.

For example:

//This is our mock dataset which has FooItem type items
val dataset = listOf<FooItem>(items)

class FooItem {

    //here your variables

    private var id : Long = 0L

    //you can handle different ids at high accuracy like;
    fun getItemId() : Long {
        if (id == 0L) {
            id = abs(Random.nextLong(2, Long.MAX_VALUE))
        }
        return id
    }

}


//Then go back your adapter and set adapter item id below like
override fun getItemId(position: Int) = dataset[position].getItemId()

With this way, you will no need to close recyclerview animations and notify all dataset.

oguzhan
  • 2,073
  • 1
  • 26
  • 23
-1

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);
        }
    }
}
César Cobo
  • 598
  • 5
  • 9
-1

in my case , this solve my issue

rv.setAdapter(null);
rv.setItemAnimator(null);

PS: my problem ocur when i do seache filter in my recyclerview adapter

hamil.Dev
  • 137
  • 2
  • 9
-2

If anyone is having the same issue, I overrided these two methods in my adapter, it worked perfectly.

        override fun getItemId(position: Int): Long {
            return position.toLong()
        }
    
        override fun getItemViewType(position: Int): Int {
            return position
        }