报错截图:
场景介绍:在使用recycleView 自动递增数据,且自动滚动到最新行; 当数据达到273条 时出现ANR;
项目中 全部的列表适配器使用的三方库:BaseRecyclerViewAdapterHelper (很早之前的项目)
代码:
public class LogRecyclerViewAdapter extends BaseQuickAdapter<LogBean,BaseViewHolder> {
public LogRecyclerViewAdapter(int layoutResId, @Nullable List<LogBean> data) {
super(layoutResId, data);
}
// 避免布局错乱
@Override
public int getItemViewType(int position) {
Log.d(TAG, "getItemViewType1111: " +position);
return position;
}
@Override
protected void convert(BaseViewHolder helper, LogBean item) {
String title = item.getTitle();
String msg = item.getMsg();
helper.setText(R.id.log_title_txt,title);
helper.setText(R.id.log_msg_txt,msg);
// 设置异常字体颜色
if(msg.contains("异常")||msg.contains("中断")||msg.contains("失败")){
helper.setTextColor(R.id.log_title_txt,Color.parseColor("#FF2C00"));
helper.setTextColor(R.id.log_msg_txt, Color.parseColor("#FF2C00"));
}
}
}
解决 :根据报错提示,百度后解决 需要注释掉重写的getItemViewType 函数,其实是一个三方库的bug;
可参考:https://blog.csdn.net/lovelixue/article/details/103641023
为知其所以然 ,继续跟踪代码;
进入适配器继承的父类:BaseQuickAdapter该类继承自RecyclerView.Adapter<K> , 找到重写的getItemViewType 函数,getItemViewType的返回值 当有其他布局(头部脚部或空布局)时候返回值各自的常量;项目只是单item 没有添加其他View 代码会走
return getDefItemViewType(adjPosition);
该方法会重新定义getItemViewType的值而该值是position变量;
@Override
public int getItemViewType(int position) {
if (getEmptyViewCount() == 1) {
boolean header = mHeadAndEmptyEnable && getHeaderLayoutCount() != 0;
switch (position) {
case 0:
if (header) {
return HEADER_VIEW;
} else {
return EMPTY_VIEW;
}
case 1:
if (header) {
return EMPTY_VIEW;
} else {
return FOOTER_VIEW;
}
case 2:
return FOOTER_VIEW;
default:
return EMPTY_VIEW;
}
}
int numHeaders = getHeaderLayoutCount();
if (position < numHeaders) {
return HEADER_VIEW;
} else {
int adjPosition = position - numHeaders;
int adapterCount = mData.size();
if (adjPosition < adapterCount) {
return getDefItemViewType(adjPosition);
} else {
adjPosition = adjPosition - adapterCount;
int numFooters = getFooterLayoutCount();
if (adjPosition < numFooters) {
return FOOTER_VIEW;
} else {
return LOADING_VIEW;
}
}
}
}
protected int getDefItemViewType(int position) {
if (mMultiTypeDelegate != null) {
return mMultiTypeDelegate.getDefItemViewType(mData, position);
}
return super.getItemViewType(position);
}
回到:LogRecyclerViewAdapter 类中 ,业务代码在convert函数中实现 跟踪该函数,发现在BaseQuickAdapter中被onBindViewHolder调用,代码如下:它会获取holder的getItemViewType ,其值的已经分析过 取自position ,每次源数据新增一条position就会随之递增;其实报错的时候,还没有执行到该函数,我们需要查看recycleView的源码 执行该方法之前还执行了onCreateViewHolder 函数。
@Override
public void onBindViewHolder(K holder, int position) {
//Add up fetch logic, almost like load more, but simpler.
autoUpFetch(position);
//Do not move position, need to change before LoadMoreView binding
autoLoadMore(position);
int viewType = holder.getItemViewType();
switch (viewType) {
case 0:
convert(holder, getItem(position - getHeaderLayoutCount()));
break;
case LOADING_VIEW:
mLoadMoreView.convert(holder);
break;
case HEADER_VIEW:
break;
case EMPTY_VIEW:
break;
case FOOTER_VIEW:
break;
default:
convert(holder, getItem(position - getHeaderLayoutCount()));
break;
}
}
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
K baseViewHolder = null;
this.mContext = parent.getContext();
this.mLayoutInflater = LayoutInflater.from(mContext);
switch (viewType) {
case LOADING_VIEW:
baseViewHolder = getLoadingView(parent);
break;
case HEADER_VIEW:
baseViewHolder = createBaseViewHolder(mHeaderLayout);
break;
case EMPTY_VIEW:
baseViewHolder = createBaseViewHolder(mEmptyLayout);
break;
case FOOTER_VIEW:
baseViewHolder = createBaseViewHolder(mFooterLayout);
break;
default:
baseViewHolder = onCreateDefViewHolder(parent, viewType);
bindViewClickListener(baseViewHolder);
}
baseViewHolder.setAdapter(this);
return baseViewHolder;
}
重点看下,switch 方法 ,会逐条遍历viewType,我们分别看case 条件值 ,分别是
public static final int HEADER_VIEW = 0x00000111;
public static final int LOADING_VIEW = 0x00000222;
public static final int FOOTER_VIEW = 0x00000333;
public static final int EMPTY_VIEW = 0x00000555;
是十六进制的魔法数字 ,case ==0 正常调用 0x00000111 转换成十进制是273 ,所以 当position 的值273的 时候,会进入第二个条件执行createBaseViewHolder (mHeaderLayout)此时的参数是null,
该函数表示通过反射的方式获取viewhold
protected K createBaseViewHolder(View view) {
Class temp = getClass();
Class z = null;
while (z == null && null != temp) {
z = getInstancedGenericKClass(temp);
temp = temp.getSuperclass();
}
K k;
// 泛型擦除会导致z为null
if (z == null) {
k = (K) new BaseViewHolder(view);
} else {
k = createGenericKInstance(z, view);
}
return k != null ? k : (K) new BaseViewHolder(view);
}
进入BaseViewHolder 类中,该类继承自RecyclerView.ViewHolder,作用是封装了各种赋值函数,通过getView 获取到item的view 去赋值等。
public ViewHolder(@NonNull View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
}
终于跟踪到报错的地方了。因为当初入参是null 因此条件判断进入itemView ==null ,就有了开始的IDE的输出报错;
完美的闭环
如有不完善的地方,譬如:recycleView源码部分,没有仔细说明,请多多担待;
抱拳;