java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position的一种规避方式

公司项目需要使用大量的RecyclerView控件,需要经受数据频繁切换的考验,导致出现了大量的
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position异常。贴一段完整的error的log看一下:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 20(offset:20).state:43
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4663)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4621)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:528)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1181)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.LinearLayoutManager.scrollHorizontallyBy(LinearLayoutManager.java:1019)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.GridLayoutManager.scrollHorizontallyBy(GridLayoutManager.java:361)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.RecyclerView.scrollByInternal(RecyclerView.java:1525)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.support.v7.widget.RecyclerView.onGenericMotionEvent(RecyclerView.java:2582)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEventInternal(View.java:9410)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEvent(View.java:9391)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:2067)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:2018)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEvent(View.java:9384)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:2067)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:2018)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEvent(View.java:9384)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:2067)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:2018)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEvent(View.java:9384)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchTransformedGenericPointerEvent(ViewGroup.java:2067)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewGroup.dispatchGenericPointerEvent(ViewGroup.java:2018)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchGenericMotionEvent(View.java:9384)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at com.android.internal.policy.PhoneWindow$DecorView.superDispatchGenericMotionEvent(PhoneWindow.java:2424)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at com.android.internal.policy.PhoneWindow.superDispatchGenericMotionEvent(PhoneWindow.java:1748)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.app.Activity.dispatchGenericMotionEvent(Activity.java:2822)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at com.android.internal.policy.PhoneWindow$DecorView.dispatchGenericMotionEvent(PhoneWindow.java:2391)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.View.dispatchPointerEvent(View.java:9522)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:4230)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4096)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3695)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3661)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3787)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3669)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3844)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3695)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3661)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3669)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3642)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5922)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5896)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5857)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:6025)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.os.MessageQueue.nativePollOnce(Native Method)
04-07 17:00:25.011 E/AndroidRuntime(20589):     at android.os.Message

这个问题比较麻烦,是RecyclerView控件的一个bug,在stackoverflow上有各种各样的复现及解决方案,
然而,对我来说并没有什么卵用,解决不了我的问题。一次偶然的机会,在看一篇博客的时候找到了灵感。
规避这个问题的关键,其实在上述log的at android.support.v7.widget.GridLayoutManager.scrollHorizontallyBy(GridLayoutManager.java:361)这一行。
解决方法,就是在该方法内直接捕获IndexOutOfBoundsException。下面给出使用场景及部分关键代码。

使用场景:RecyclerView作为一个可以横向滑动的网状列表,数据频繁的切换,需要经常进行Adapter.notifyDataSetChanged()操作。
解决方案:

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 *
 * Created by jxl on 2017/4/8.
 */
public class OnegoGridLayoutManager extends GridLayoutManager {
     

    public OnegoGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public OnegoGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public OnegoGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            return super.scrollHorizontallyBy(dx, recycler, state);
        } catch (IndexOutOfBoundsException e) {
            Log.e("hello", "scrollHorizontallyBy, IndexOutOfBoundsException");
            e.printStackTrace();
            return 0;
        }
    }
}

然后在使用RecyclerView控件的时候,使用上面这个自定义LayoutManager即可。

如果使用场景有别于此处,比如RecyclerView控件需要上下滑动而不是左右滑动的列表样式,那么可以自定义LayoutManager继承自LinearLayoutManager,重写该类的onLayoutChildren方法,在方法内捕获异常。

说到底,其实并没有解决这个问题,只是通过捕获异常的方式使程序不至于崩溃。这样做可能带来一些数据异常的问题,所以有需要的话,还是慎重起见。
最后,感谢
http://blog.csdn.net/lovexieyuan520/article/details/50537846
这篇文章带来的灵感。
以上。

你可能感兴趣的:(bug处理,异常,控件,android,recyclerview)