崩溃原因:
出现这个问题的主要原因是,对RecyclerView的数据操作混乱导致的。比如notifyDataSetChanged后还没结束又调用 notifyItemInserted、notifyItemMoved等CRUD方法,造成数据的不一致。请仔细检查对数据集操作的事件流是否存在问题。
复现步骤:
- A界面recyclerview列表显示一些数据,比如10个数据
- B处对A界面recyclerview列表数据进行删减,比如还剩7个数据
- 回到A界面还是显示之前的10个数据,但是超过当前实际7个数据
解决方案:
一、捕获异常
自定义RecyclerView LayoutManager的包装类,比如在LinearLayoutManager的onLayoutChildren()方法里try-catch捕获该异常。
import android.content.Context;
import android.util.AttributeSet;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
public WrapContentLinearLayoutManager(Context context) {
super(context);
}
public WrapContentLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public WrapContentLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}
复制代码
二、数据集一致
在进行数据移除和数据增加时,需要保证RecyclerView的Adapter中的数据集和移除、添加等操作后的数据集保持一致!这里,前者是在该Adapter内部,假设叫做内部数据集,后者是开发人员传过给Adapter的,假设叫外部数据集。更新RecyclerView数据时,需要保证外部数据集和内部数据集实时保持一致。 外部数据集同步到内部数据集,一般使用下面的方法:
- notifyItemRangeRemoved();
- notifyItemRangeInserted();
- notifyItemRangeChanged();
- notifyDataSetChanged();
需要注意的是,使用notifyDataSetChange()方法更新内部数据集,没有默认的动画效果,同时更新数据的效率页不如上面的方法,官方不推荐使用这种方式更新数据集。
举例来说明,在RecyclerView的Adapter里,写一段发生异常的错误代码,如下:
public void notifyData(List<PoiItem> poiItemList) {
if (poiItemList != null ) {
mPoiItems.clear();
mPoiItems.addAll(poiItemList);
notifyItemRangeChanged(0, poiItemList.size());
}}
复制代码
错误分析:mPoiItems是外部数据集,对该外部数据集做了两个操作:先移除数据,然后添加数据,之后notify数据集。这里,添加数数据时(Adapter的内部数据集内容还处在外部数据集移除数据之前),造成了内部和外部数据集不一致。 这是一方面,另一方面使用了notifyItemRangeChanged()来更新数据,如果poiItemList传过来的新数据和原来mPoiItems的数据数量不一致,就会出现内部数据集和外部数据在同步后不一致,从而报错。 综上修复后,运行正常的代码如下:
public void notifyData(List<PoiItem> poiItemList) {
if (poiItemList != null) {
int previousSize = mPoiItems.size();
mPoiItems.clear();
notifyItemRangeRemoved(0, previousSize);
mPoiItems.addAll(poiItemList);
notifyItemRangeInserted(0, poiItemList.size());
}}
复制代码
参考: