有关List的throwIndexOutOfBoundsExce

作者: super_shanks | 来源:发表于2017-09-06 17:50 被阅读457次
    Wechat :DSGtalk1989

    throwIndexOutOfBoundsException各位应该见的比较多了,通常的问题都是处在ArrayList中本身的size只有x, 结果你取了x + N位的值,导致系统直接叫你滚。见得比较多的是我们没有及时的处理刷新的问题,或者轮询删减的时候没有处理好,再或者说你就是写了个bug(这么说肯定对的。。)。
    今天介绍的是开发过程中碰到比较诡异的技能盲点,听我道来。

    接手了之前革命先烈留下的一个项目,使用的是ListView,并且项目中多处使用到,千丝万缕,短时间之内难以重构成RecyclerView,因此之中的header,footer坑就特别难缠。

    经过一轮修复,发现基本解决了header和footer的问题。但是bugly上依然有很多用户出现如下的问题

    java.lang.IndexOutOfBoundsException
    Invalid index 0, size is 0
    java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
    
    不知道各位对bug fix这个过程是如何看待的,我个人认为解bug的过程与开发过程的收益比基本维持在3:1。
    记得之前在一家公司,有个哥们专门申请希望可以转职清理bug,但是领导好像没有批。。
    

    在bug fix的道理上,最坑的是没有log,其次是so崩溃,再其次是有log无定位,上面的情况是第三种,前两种更恶心的之后有空跟大家一起聊聊。

    如果你直接希望能从log里面找出什么端倪,我可以贴出来你试一试。

    1 java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:255)
    2 java.util.ArrayList.get(ArrayList.java:308)
    3 android.widget.HeaderViewListAdapter.isEnabled(HeaderViewListAdapter.java:164)
    4 android.widget.ListView.dispatchDraw(ListView.java:3430)
    5 android.view.View.draw(View.java:15627)
    6 android.widget.AbsListView.draw(AbsListView.java:4644)
    7 android.view.View.updateDisplayListIfDirty(View.java:14504)
    8 android.view.View.getDisplayList(View.java:14533)
    9 android.view.View.draw(View.java:15324)
    10 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    11 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    12 android.view.View.updateDisplayListIfDirty(View.java:14496)
    13 android.view.View.getDisplayList(View.java:14533)
    14 android.view.View.draw(View.java:15324)
    15 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    16 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    17 android.view.View.updateDisplayListIfDirty(View.java:14496)
    18 android.view.View.getDisplayList(View.java:14533)
    19 android.view.View.draw(View.java:15324)
    20 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    21 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    22 android.view.View.draw(View.java:15627)
    23 android.support.v4.view.ViewPager.draw(ViewPager.java:2341)
    24 android.view.View.updateDisplayListIfDirty(View.java:14504)
    25 android.view.View.getDisplayList(View.java:14533)
    26 android.view.View.draw(View.java:15324)
    27 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    28 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    29 android.view.View.updateDisplayListIfDirty(View.java:14496)
    30 android.view.View.getDisplayList(View.java:14533)
    31 android.view.View.draw(View.java:15324)
    32 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    33 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    34 android.view.View.updateDisplayListIfDirty(View.java:14496)
    35 android.view.View.getDisplayList(View.java:14533)
    36 android.view.View.draw(View.java:15324)
    37 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    38 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    39 android.view.View.updateDisplayListIfDirty(View.java:14496)
    40 android.view.View.getDisplayList(View.java:14533)
    41 android.view.View.draw(View.java:15324)
    42 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    43 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    44 android.view.View.updateDisplayListIfDirty(View.java:14496)
    45 android.view.View.getDisplayList(View.java:14533)
    46 android.view.View.draw(View.java:15324)
    47 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    48 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    49 android.view.View.updateDisplayListIfDirty(View.java:14496)
    50 android.view.View.getDisplayList(View.java:14533)
    51 android.view.View.draw(View.java:15324)
    52 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    53 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    54 android.view.View.updateDisplayListIfDirty(View.java:14496)
    55 android.view.View.getDisplayList(View.java:14533)
    56 android.view.View.draw(View.java:15324)
    57 android.view.ViewGroup.drawChild(ViewGroup.java:3536)
    58 android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
    59 com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2845)
    60 android.view.View.draw(View.java:15627)
    61 android.widget.FrameLayout.draw(FrameLayout.java:658)
    62 com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2820)
    63 android.view.View.updateDisplayListIfDirty(View.java:14504)
    64 android.view.View.getDisplayList(View.java:14533)
    65 android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
    66 android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
    67 android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
    68 android.view.ViewRootImpl.draw(ViewRootImpl.java:2987)
    69 android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2801)
    70 android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2412)
    71 android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1320)
    72 android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6733)
    73 android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
    74 android.view.Choreographer.doCallbacks(Choreographer.java:603)
    75 android.view.Choreographer.doFrame(Choreographer.java:572)
    76 android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
    77 android.os.Handler.handleCallback(Handler.java:815)
    78 android.os.Handler.dispatchMessage(Handler.java:104)
    79 android.os.Looper.loop(Looper.java:194)
    80 android.app.ActivityThread.main(ActivityThread.java:5869)
    81 java.lang.reflect.Method.invoke(Native Method)
    82 java.lang.reflect.Method.invoke(Method.java:372)
    83 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1019)
    84 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:814)
    

    OK,如果你放弃了,那我们继续。

    ArrayList使用到最多的地方应该就是列表的data类,我们再看一下,出现问题的这一行:

    Invalid index 0, size is 0
    

    size已经为空了,我们还去取它的第一位值。我想你肯定不会去这么写,到底在什么情况下会出现这样的问题
    换一种说法,列表里什么都没有了,你还想要去里面看看有什么。有这两种可能:

    • 列表里什么都没有设置,你就先去取了。
    • 列表清空了,你不知道。

    我认为两种假设的性质是一样的,咱们基本都不会去做,但是不知道你们是否会在代码中使用到如下的adapter调用方式

    list.clear();
    list.addAll(items);
    adapter.notifyDataSetChanged();
    

    乍一看没什么问题,我们为了保持列表中数据的新鲜度,基本在很多情况下都会去将整个列表清空再重新加载,然后去刷新列表,除非你有专门针对做过数据diff,从而来刷新指定item,否则大家都会这么去玩。
    本身无可厚非,但是在极端情况下就会出现上面所说的情况。

    一旦clear和addAll方法放在了异步线程中处理,那么就给了ListView有机可趁,让他随时有机会来访问我们没有上锁的数据,面上看起来,清空了数据,再把新数据都加进去,然后通知界面更新。

    实际上,当我们在进行clear操作了之后还没有addAll之前,如果ListView调用了draw方法,那就会立马崩溃。

    所以我们一旦出现需要对listView的数据源进行操作,必须要放在UI线程中,或者抛出message交由handler放在handleMessage中进行处理,保证数据源处理和ListView的方法在同一个线程中同步执行,就不会出现这种奇怪的数组越界错误了。


    总结

    ListView的数据改变也需要遵从同步的原则,尽量避免在异步线程中操作与UI线程有关的,或者会被UI线程调用的数据。

    相关文章

      网友评论

        本文标题:有关List的throwIndexOutOfBoundsExce

        本文链接:https://www.haomeiwen.com/subject/aipjcxtx.html