惊现RecyclerView内部Bug???别急,我们慢慢解决它~

简介: 写在前面这也是久违的一整个月没有写 Blog,也是由于近期给妹纸找工作,各种坑蒙拐骗,然而都没卵用。额,广大朋友们,成都需要软件测试、线上运维、产品助理的伙伴,赶紧私聊我了。

写在前面

这也是久违的一整个月没有写 Blog,也是由于近期给妹纸找工作,各种坑蒙拐骗,然而都没卵用。额,广大朋友们,成都需要软件测试、线上运维、产品助理的伙伴,赶紧私聊我了。这妹纸,学习能力挺好,资质也不错,专业成绩总体排名年级第二,保送研究生(近期已决定放弃),心动不如行动,晚了就没机会了,赶紧私信我吧。

惊现 RecyclerView 内部 bug?

扯淡就不扯淡了,咱们还是说说这个早就可能被写烂吐槽的 RecyclerView 的 bug 吧。

不知道你们遇见没有,在 RecyclerView 被推的如火如荼的时候,你喜欢它,你默默用它,甚至对它的健壮性爱不释手。你觉得,这玩意儿都出来这么久了,一定没问题。额,没毛病。然而,在某一次快速滑动中,Boom,崩溃了!瞬间打脸。

查看 Log 得到下面的玩意儿。

 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
        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)

杂一看像是数组越界?NO NO NO,这日志看上去根本就跟我们代码无关呀。多番 Google 发现,这貌似是 Google 程序员的锅?内部 bug?这TM官方的问题,关你何事?要不咱们不用 RecyclerView 了吧?

你是一个优秀的程序猿,不应该总是逃避问题,而应该思考如何去解决它。不过这说明了一个问题,人非圣贤孰能无过,连 Google 程序员那么牛逼的存在都会出问题,我们是不是......嘿嘿。

这玩意儿崩溃的原因比较清楚,就是如果绑定的集合List中的数据和 RecycerView 的数据不一致的时候,调用更新方法的时候会复现。

怎么解决?

有人这么说,造成崩溃的原因极有可能是当 clear 了之后,迅速上滑,但由于新数据还没来,导致 RecyclerView 需要更新加载下面的 Item 的时候,找不到数据源,导致了崩溃的发生。

所以,既然如此,一定可以通过让 Clear 的时候,禁止 RecyclerView 的滑动来解决它。代码如下:

private boolean mIsRefreshing=false;
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;

其它人的意见

人,想法,总是千奇百怪。

造成崩溃的原因其实很明显,如果你更新集合 List 后,调用 RVAdapter 的 notifyXXXX 方法时,adapter 的更新预期接口和实际集合更新结果不同,就会出现这个异常!不信你可以随便模拟这个情况的发生。

所以有人就得到了这样的结论:

  • RVAdapter 的 notifyDataSetChanged 方法执行后,在一定时间内,如果你更新了你的集合(无论是否在主线程更新集合),那么这个更新会实时反应到控件上,也就是说你的控件显示也会更新。

  • 调用诸如 notifyItemRangeInserted 这样的方法之前,考虑清楚你的集合到底更新成什么样了!要注意参考结论1,结论1会影响你的判断。

解决该问题的正确姿势?

显然,上面的方法都不太好用,继续研究发现,直接采用下面的方法可以很好的解决。

经过多番研究发现,直接像下面这样,可以完美解决我们的问题。

1、复写 LinearLayoutManager

package com.zxedu.ischool.common;

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;

/**
* Author: nanchen
* Email: liushilin520@foxmail.com
* Date: 2017-05-19  15:56
*/

public class WrapContentLinearLayoutManager extends LinearLayoutManager {
   public WrapContentLinearLayoutManager(Context context) {
       super(context);
   }

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

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

对,没错,直接更换LayoutManaer就OK了

//        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
         // 解决RecyclerView可能出现的holder数组越界Bug
        mRecyclerView.setLayoutManager(new WrapContentLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

写在最后

请别问我为什么这样就能解决?我会大声告诉你,我也不知道!

我能怎么办,我也很无奈~


做不完的开源,写不完的矫情。欢迎扫描下方二维码或者公众号搜索「nanchen」关注我的微信公众号,目前多运营 Android ,尽自己所能为你提升。如果你喜欢,为我点赞分享吧~


nanchen
目录
相关文章
|
测试技术
解决Bug应有的心态和解决方法的一些思路、方法和心得
永远要相信程序是不会骗你的,是自己在处理理逻辑中出问题,而在特定的环境中才会出现或者是自己压根就想不到情况下出现。 前几天在处理一个接口任务时,在测试环境跑是一点都没有,但在正式环境却没有将数据拉下来。没有报任何错误,一度怀疑、抱怨! 还好最后找到问题解决了!
113 0
终于搞明白什么叫做回调了
终于搞明白什么叫做回调了
70 0
|
算法 NoSQL API
到底该不该看源码(懂这三点儿就够了)
1、不要为了看源码而看源码 2、代码积累到一定程度,遇到问题自然就去查源码了,然后你就看懂了 3、两年内不要刻意去看源码,可以点开简单了解一下就行,前两年疯狂做项目就行了,后期项目做的多了,你自己就会有疑问,每次写代码就会问自己为什么要这样写?底层的原理是什么?很自觉的带着问题就去看源码了,如果你没有这样的疑问,那说明你也不适合去看源码了,写写业务代码,了了一生
214 0
|
Java 中间件 程序员
最网最全bug定位套路,遇见bug再也不慌了
最网最全bug定位套路,遇见bug再也不慌了
367 0
|
设计模式 算法 搜索推荐
长痛不如短痛,这次重构代码我干掉了 if-else
长痛不如短痛,这次重构代码我干掉了 if-else
185 0
|
编译器
为何代码没错,却出不来结果,你可能...
为何代码没错,却出不来结果,你可能...
143 0
为何代码没错,却出不来结果,你可能...
|
存储 Windows
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
293 0
明明很努力的写博客,为啥没人看?试试这些方法和工具(建议收藏)
|
缓存
再也不用担心面试官问RecycleView了
关于RecyclerView,之前我写过一篇比较基础的文章,主要说的是缓存和优化等问题。但是有读者反映问题不够实际和深入。于是,我又去淘了一些关于RecyclerView的面试真题,大家一起看看吧,这次的问题如果都弄懂了,下次面试再遇到RecyclerView应该就没啥可担心的了。
267 0
|
设计模式 安全 IDE
Context都没弄明白,还怎么做Android开发?
什么是 Context? 四大组件和 Context Application 和 Context 为什么 Application 的 Context 不可以创建 Dialog ?
|
存储 机器人
我决定,要把bug留给子孙后代了,让厉害的人继承吧
我决定,要把bug留给子孙后代了,让厉害的人继承吧