返回

解开RecyclerView的两个非传统型崩溃之谜

Android

在Android开发中,RecyclerView作为必不可少的列表控件,为开发者提供了高效、灵活的列表展示解决方案。然而,在使用过程中,RecyclerView往往会出现一些不为人知的崩溃,让开发者感到困惑和沮丧。本文将为大家揭示RecyclerView中两个非传统型崩溃的成因与解决方法,希望能对各位读者有所帮助。

崩溃一: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 androidx.recyclerview.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:6105)
    at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5937)
    at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPositionAndType(RecyclerView.java:5976)
    at androidx.recyclerview.widget.RecyclerView.findViewHolderForPosition(RecyclerView.java:3576)
    at androidx.recyclerview.widget.RecyclerView.findContainingItemView(RecyclerView.java:3856)
    at androidx.recyclerview.widget.RecyclerView$5.performClick(RecyclerView.java:2678)
    at android.view.View.performClickInternal(View.java:5723)
    at android.view.View.access$300(View.java:753)
    at android.view.View$PerformClick.run(View.java:23128)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

成因分析

该崩溃的根源在于RecyclerView的缓存机制。当RecyclerView滚动时,为了提高性能,它会将滑出屏幕的item缓存起来,以便在需要时快速复用。然而,如果在item被缓存后,又对该item进行了某些操作(例如,调用了item的setOnClickListener方法),那么在复用该item时,就会引发崩溃。这是因为,此时该item的状态与被缓存时的状态不一致,导致RecyclerView无法正确处理。

解决方案

要解决该崩溃,需要在对item进行操作后,调用item的setRecyclable(false)方法,以防止该item被缓存起来。例如,在为item设置点击事件时,可以这样写:

itemView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        // do something
        itemView.setRecyclable(false);
    }
});

崩溃二:java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling

崩溃场景 :在RecyclerView的Adapter中,在onBindViewHolder方法中调用notifyDataSetChanged()方法,导致崩溃。

成因分析

该崩溃的原因是,在RecyclerView正在计算布局或滚动时,调用了notifyDataSetChanged()方法。这会导致RecyclerView出现不一致的状态,从而引发崩溃。

解决方案

要解决该崩溃,需要确保在调用notifyDataSetChanged()方法时,RecyclerView处于空闲状态。可以通过以下方法来做到这一点:

  • 在Adapter中,重写onDetachedFromRecyclerView()方法,并在该方法中调用notifyDataSetChanged()方法。
  • 在RecyclerView的LayoutManager中,重写canScrollVertically()和canScrollHorizontally()方法,并在这些方法中返回false。
  • 在RecyclerView的ItemAnimator中,重写animateAdd()、animateRemove()、animateMove()和animateChange()方法,并在这些方法中返回false。

以上就是RecyclerView中两个非传统型崩溃的成因与解决方法。希望本文能够帮助大家更好地理解RecyclerView的缓存机制,并在开发过程中避免这些崩溃的发生。