使用RecyclerView的兩個非傳統型崩潰

TheShy_發表於2017-11-02

1. Scrapped or attached views may not be recycled. isScrap:false isAttached:true

崩潰場景 : 一個普通的RecyclerView列表,點選某個item進入詳情頁,然後返回時崩潰(bug日誌如下).發現比較難尋找到具體原因,於是搜尋了大谷歌爸爸.

java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true
at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:5659)
    at android.support.v7.widget.RecyclerView$Recycler.recycleView(RecyclerView.java:5603)
    at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:277)
    at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:324)
    at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:337)
    at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:344)
    at android.support.v7.widget.GapWorker.run(GapWorker.java:370)
    at android.os.Handler.handleCallback(Handler.java:743)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:150)
    at android.app.ActivityThread.main(ActivityThread.java:5665)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:822)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:712)複製程式碼

發現大部分導致這個bug的原因是:android:animateLayoutChanges與RecyclerView重新整理共用.

大致意思是:這個錯誤的原因是在你的xml佈局檔案中將android:animateLayoutChanges 設定為了true 並且java 程式碼裡對RecyclerView 的adapter 呼叫了notifyDataSetChanged() 方法。
會報錯原因分析:
android:animateLayoutChanges="true"是在列表增刪Item的時候呼叫系統自帶的動畫效果,而RecyclerView的機制是對子檢視的複用,不會在真正意義上對Item的控制元件進行增加、刪除,與android:animateLayoutChanges原理相悖,因此會報錯。
另一條高贊回答顯示 : 我們正在構建的RecyclerView有不止一種型別的檢視,有些子view在有EditText時候。過了一會兒,我們將問題固定在與焦點有關的問題上。在回收EditTexts時,會發生此錯誤,當新的資料被繫結到一個迴圈的檢視時,我們嘗試清除焦點,但直到android:focusableInTouchMode =“true”被設定在RecycleView上才起作用。
但是,我根本沒有用到animateLayoutChanges這個屬性,所以上面的對我來說並不適用.後來經過思考.發現我每次在返回重新重新整理頁面時候,發生的崩潰.排查程式碼發現每次重新整理前會清空list,然後請求網路,而從清空資料到獲取到資料的這段時間裡,List中的資料是不存在的,所以給了RecyclerView要回收Item,但是View沒有被回收的假象(此時並沒有執行notifyDataSetChanged()方法),因此程式報錯。

其解決方法是:將列表清空的方法放到獲取到介面資料以後執行。

第二種解決方案:
繼承你要用的layoutmanager,捕獲異常 程式碼如下:

@Override
public void collectAdjacentPrefetchPositions(int dx, int dy, RecyclerView.State state, LayoutPrefetchRegistry layoutPrefetchRegistry) {
    try {
        super.collectAdjacentPrefetchPositions(dx, dy, state, layoutPrefetchRegistry);
    } catch (IllegalArgumentException e) {
        LogUtils.e("catch exception");
    }
}複製程式碼

2.Called attach on a child which is not detached: ViewHolder{41ce22c0 position=4 id=-1, oldPos=-1, pLpos:-1 no parent}

If you're using notifyItemChanged(position) try changing it for notifyDataSetChanged(). This could be a bug with notifyItemChanged. Try dispatching all events as soon as they happen as well, since RecyclerView already bundles the updates by default.

相關文章