【知識整理】驚現RecyclerView內部Bug???別急,我們慢慢解決它~


一、寫在前面

       這也是久違的一整個月沒有寫Blog,也是由於近期給妹紙找工作,各種坑蒙拐騙,然而都沒卵用。額,廣大朋友們,成都需要軟件測試、線上運維、產品助理的伙伴,趕緊私聊我了。這妹紙,學習能力挺好,資質也不錯,專業成績總體排名年級第二,保送研究生(近期已決定放棄),心動不如行動,晚了就沒機會了,趕緊私信我吧。

  該文章同步發布到:

      簡書:http://www.jianshu.com/p/d520e6559433

      CSDN:http://blog.csdn.net/nanchen_lsl/article/details/72829093

二、驚現 RecyclerView 內部bug?

      扯淡就不扯淡了,咱們還是說說這個早就可能被寫爛吐槽的RecyclerView的bug吧。

      不知道你們遇見沒有,在RecyclerView被推的如火如荼的時候,你喜歡它,你默默用它,甚至對它的健壯性(艹,這樣也算是性、愛)愛不釋手。你覺得,這玩意兒都出來這么久了,一定沒問題。額,沒毛病。然而,在某一次快速滑動中,Boom,崩潰了!瞬間打臉。

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

 1 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
 2         at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300)
 3         at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
 4         at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
 5         at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
 6         at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
 7         at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093)
 8         at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956)
 9         at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715)
10         at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
11         at android.view.Choreographer.doCallbacks(Choreographer.java:555)
12         at android.view.Choreographer.doFrame(Choreographer.java:524)
13         at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
14         at android.os.Handler.handleCallback(Handler.java:615)
15         at android.os.Handler.dispatchMessage(Handler.java:92)
16         at android.os.Looper.loop(Looper.java:137)
17         at android.app.ActivityThread.main(ActivityThread.java:4921)
18         at java.lang.reflect.Method.invokeNative(Native Method)
19         at java.lang.reflect.Method.invoke(Method.java:511)
20         at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
21         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
22         at dalvik.system.NativeStart.main(Native Method)

    雜一看像是數組越界?NO NO NO,這日志看上去根本就跟我們代碼無關呀。多番Google發現,這貌似是Google程序員的鍋?內部bug?這TM官方的問題,關你何事?要不咱們不用RecyclerView了吧?

    你是一個優秀的程序猿,不應該總是逃避問題,而應該思考如何去解決它。不過這說明了一個問題,人非聖賢孰能無過,連Google程序員那么牛逼的存在都會出問題,我們是不是......嘿嘿。

    這玩意兒崩潰的原因比較清楚,就是如果綁定的集合List中的數據和RecycerView的數據不一致的時候,調用更新方法的時候會復現。

三、怎么解決?

    有人這么說,造成崩潰的原因極有可能是當clear了之后,迅速上滑,但由於新數據還沒來,導致RecyclerView需要更新加載下面的Item的時候,找不到數據源,導致了崩潰的發生。

    所以,既然如此,一定可以通過讓Clear的時候,禁止RecyclerView的滑動來解決它。代碼如下:

 1 private boolean mIsRefreshing=false;
 2 mRecyclerView.setOnTouchListener(
 3     new View.OnTouchListener() {
 4       @Override
 5       public boolean onTouch(View v, MotionEvent event) {
 6         if (mIsRefreshing) {
 7           return true;
 8         } else {
 9           return false;
10         }
11       }
12     }
13 );
14 //當刷新時設置
15 //mIsRefreshing=true;
16 //刷新完畢后還原為false
17 //mIsRefreshing=false;

四、其它人的意見

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

      造成崩潰的原因其實很明顯,如果你更新集合List后,調用RVAdapter的notifyXXXX方法時,adapter的更新預期接口和實際集合更新結果不同,就會出現這個異常!不信你可以隨便模擬這個情況的發生。

      所以有人就得到了這樣的結論:

      1、RVAdapter的notifyDataSetChanged方法執行后,在一定時間內,如果你更新了你的集合(無論是否在主線程更新集合),那么這個更新會實時反應到控件上,也就是說你的控件顯示也會更新。

      2、調用諸如notifyItemRangeInserted這樣的方法之前,考慮清楚你的集合到底更新成什么樣了!要注意參考結論1,結論1會影響你的判斷。

五、解決該問題的正確姿勢?

     顯然,上面的方法都不太好用,繼續研究發現,直接采用下面的方法可以很好的解決。

     經過多番研究發現,直接像下面這樣,可以完美解決我們的問題。

    1、復寫LinearLayoutManager

 1 package com.zxedu.ischool.common;
 2 
 3 import android.content.Context;
 4 import android.support.v7.widget.LinearLayoutManager;
 5 import android.support.v7.widget.RecyclerView;
 6 import android.util.AttributeSet;
 7 
 8 /**
 9  * Author: nanchen
10  * Email: liushilin520@foxmail.com
11  * Date: 2017-05-19  15:56
12  */
13 
14 public class WrapContentLinearLayoutManager extends LinearLayoutManager {
15     public WrapContentLinearLayoutManager(Context context) {
16         super(context);
17     }
18 
19     public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
20         super(context, orientation, reverseLayout);
21     }
22 
23     public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
24         super(context, attrs, defStyleAttr, defStyleRes);
25     }
26 
27     @Override
28     public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
29         try {
30             super.onLayoutChildren(recycler, state);
31         } catch (IndexOutOfBoundsException e) {
32             e.printStackTrace();
33         }
34     }
35 }

       2、對,沒錯,直接更換LayoutManaer就OK了

1 //        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
2         // 解決RecyclerView可能出現的holder數組越界Bug
3         mRecyclerView.setLayoutManager(new WrapContentLinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

六、寫在最后

     請別問我為什么這樣就能解決?我會大聲告訴你,我也不知道!

     我能怎么辦,我也很無奈~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM