RecyclerView滑动过程中刷新数据导致的Crash

今天遇到的一个Bug,之前的项目中也遇到过,这里纪录一下,如果下次再遇到,可以直接拿来用。这个算是Android官方的一个Bug,迟迟没有解决,网上可以看到一大群人遇到该问题。

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 21(offset:21.state:20 at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300) at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258) at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803) at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302) at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265) at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093) at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956) at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725) at android.view.Choreographer.doCallbacks(Choreographer.java:555) at android.view.Choreographer.doFrame(Choreographer.java:524) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711) at android.os.Handler.handleCallback(Handler.java:615) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:4921) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794) at dalvik.system.NativeStart.main(Native Method)

复现步骤是,对有数据的列表刷新操作,在还未刷出数据的时候,不断的滑动,Crash。
很容易理解,也就是在Recyclerview滑动的时候,执行notifyDataSetChanged()导致的。
网上有很多人遇到了这个问题,回答也是千奇百怪。
自己继承LinearLayoutManager 复写下面的方法,返回false,思路是不执行部分动画。
@Overridepublic boolean supportsPredictiveItemAnimations() { return false; }

然而然并卵,依然会crash。
还有很多人回答在数据add,clear的时候马上执行notifyDataSetChanged 然并卵,不管你什么时候执行,都会crash。
刷新的时候不让用户滑动 这种方式肯定是可以的,屏蔽掉了滑动事件,肯定不会有这个冲突。
mRecyclerView.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (mIsRefreshing) { return true; } else { return false; } } } ); //当刷新时设置 //mIsRefreshing=true; //刷新完毕后还原为false //mIsRefreshing=false;

但是代价太大,太影响用户体验。产品肯定不会同意,Pass。
最后当然是大招了 在执行notifyDataSetChanged之前,现判断当前时候还在滑动,如果没有滑动,执行notifyDataSetChanged方法。
if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_IDLE || !mRecyclerView.isComputingLayout()) { mAdapter.notifyDataSetChanged(); }

【RecyclerView滑动过程中刷新数据导致的Crash】这种方式在用户没有滑动操作的时候刷新,有滑动,也不用怕,因为数据已经替换为线上返回的数据,在用户滑动的时候依然会刷新。
亲测,可行。

    推荐阅读