Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DiffResult dispatching lead to 'Inconsistency detected. Invalid view holder adapter positionViewHolder' error sometimes

I have an RxJava2 Observable that takes two lists, calculate diff result for them and send this data to adapter. Adapter dispatch updates on Main Thread.

Code of dispatching in adapter:

 public void dispatchStreams(List<StreamV3> streams, @Nullable DiffUtil.DiffResult diffResult) {

    if (streams == null) return;

    streamsList.clear();
    streamsList.addAll(streams);

    if (diffResult != null) {
        diffResult.dispatchUpdatesTo(this);
    }
}

I've got 'Inconsistency detected. Invalid view holder adapter positionViewHolder' error sometime on some devices. And I can't figure out what's wrong with my code. Min SDK 21, Target SDK 26, RecyclerView version is 26.0.0. I know about workaround with extending LinearLayoutManager and silently catching this error but this is bad solution and I believe here should be better one.

Could anyone provide any help please?

like image 808
Olexii Muraviov Avatar asked Oct 04 '17 11:10

Olexii Muraviov


2 Answers

I found a solution for this issue in this answer

It seems that issue is caused by supportsPredictiveItemAnimations property on layout managers. When I set it to false no crash happens anymore.

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;
 }
}
like image 149
Olexii Muraviov Avatar answered Oct 19 '22 15:10

Olexii Muraviov


Got into this issue while writing a Rx RV Diff to run in the background thread. Setting the supportsPredictiveItemAnimations to false is a workaround that prevents the crash but does not really solve the problem.

What was causing this exception on my case was the mutation of the data set in the background thread.

// Diff and update the RV asynchronously
fun update(list: List<D>) {
    Observable
        .create<Pair<List<D>, DiffUtil.DiffResult>> {
            // runs it asynchronous
            val result = DiffUtil.calculateDiff(
                diffCallback.apply {
                    newList = list
                }
            )

            it.onNext(Pair(diffCallback.newList, result))
            it.onComplete()
        }
        .takeUntil(destroyObservable) // observe the Lifecycle of the Frag
        .subscribeOn(Schedulers.computation()) // run it async
        .observeOn(AndroidSchedulers.mainThread()) // jump to the main thread
        .subscribe {
            // Set the new list
            dataSet = it.first.map { it }
            it.second.dispatchUpdatesTo(this@ListComponentAdapter)
        }
}
like image 38
Juliano Moraes Avatar answered Oct 19 '22 16:10

Juliano Moraes