RecyclerView and java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder in Samsung devices
This problem is caused by RecyclerView
Data modified in different thread. The best way is checking all data access. And a workaround is wrapping LinearLayoutManager
.
Previous answer
There was actually a bug in RecyclerView and the support 23.1.1 still not fixed.
For a workaround, notice that backtrace stacks, if we can catch this Exception
in one of some class it may skip this crash. For me, I create a LinearLayoutManagerWrapper
and override the onLayoutChildren
:
public class WrapContentLinearLayoutManager extends LinearLayoutManager { //... constructor @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { try { super.onLayoutChildren(recycler, state); } catch (IndexOutOfBoundsException e) { Log.e("TAG", "meet a IOOBE in RecyclerView"); } }}
Then set it to RecyclerView
:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recycler_view);recyclerView.setLayoutManager(new WrapContentLinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false));
Actually catch this exception, and seems no any side-effect yet.
Also, if you use GridLayoutManager
or StaggeredGridLayoutManager
you must create a wrapper for it.
Notice: The RecyclerView
may be in a wrong internal state.
This is an example for refreshing data with completely new content.You can easily modify it to fit your needs.I solved this in my case by calling:
notifyItemRangeRemoved(0, previousContentSize);
before:
notifyItemRangeInserted(0, newContentSize);
This is the correct solution and is also mentioned in this post by an AOSP project member.
I faced this issue once, and I solved this by wrapping the LayoutManager
and disabling predictive animations.
Here an example:
public class LinearLayoutManagerWrapper extends LinearLayoutManager { public LinearLayoutManagerWrapper(Context context) { super(context); } public LinearLayoutManagerWrapper(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public LinearLayoutManagerWrapper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean supportsPredictiveItemAnimations() { return false; }}
And set it to RecyclerView
:
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManagerWrapper(context, LinearLayoutManager.VERTICAL, false);