解开RecyclerView的两个非传统型崩溃之谜
2024-02-19 16:06:25
在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的缓存机制,并在开发过程中避免这些崩溃的发生。