现状
- 在嵌套使用时,如果RecyclerView的adapter有1000条数据,则RecyclerView会创建1000个ViewHolder,每个ViewHolder会持有一个itemView。ViewHolder和itemView的快速大量创建容易anr
问题分析
- 最终发现LinearLayoutManager中的fill方法会导致adapter中的onCreateViewHolder方法调用,
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
....
// 这个地方为true会导致adapter的onCreateViewHolder调用,正常情况下layoutState.mInfinite为false,影响recycleView子childView数量的是
// remainingSpace > 0和layoutState.hasMore(state)
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
.....
}
}
-
如上代码所示 while循环的条件有三个,正常的使用recycleView(不用scrollview嵌套reycleView),layoutState.mInfinite为false,嵌套的时候layoutState.mInfinite为true,我们看看ayoutState.mInfinite的解释
- 从注释来看,mInfinite在它为true时,LinearLayoutManager是不会限制recycleView的childView的个数,从而导致recycleview复用失效
-
再看看mInfinite的赋值
- 继续跟踪看看OrientationHelper的实现
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
.......
@Override
public int getEnd() {
return mLayoutManager.getHeight();
}
@Override
public int getMode() {
return mLayoutManager.getHeightMode();
}
......
};
}
- 可以看到OrientationHelper的getEnd()和getMode()最后都是受mLayoutManager的HeightSepc(里面是height的size和heightMode)影响
解决方案
- 自定义view,并继承RecyclerView,重写onMeasure方法,对heightSpec进行判断,如果是是 MeasureSpec.UNSPECIFIED模式,改为MeasureSpec.AT_MOST
public class CustomRecyclerView extends RecyclerView {
public CustomRecyclerView(@NonNull Context context) {
super(context);
}
public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public CustomRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
//MeasureSpec.UNSPECIFIED 模式会导致LinearLayoutManager中的fill方法中判断是否可以无限制填冲整个recycleview为true,从而导致RecyclerView的复用失效
if (MeasureSpec.getMode(heightSpec) == MeasureSpec.UNSPECIFIED) {
int size = MeasureSpec.getSize(heightSpec);
heightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.AT_MOST);
}
super.onMeasure(widthSpec, heightSpec);
}
}