转发请注明出处 : http://blog.youkuaiyun.com/snailbaby_soko/article/details/54342111
使用 RecyclerView 遇到的 Inconsistency detected 异常
(首先描述一下我当时项目的背景,我在一个RecyclerView的底部自己添加了一个布局,但这个布局是在 getItemCount >1的时候才会出现,= 0 的时候就隐藏的。简单来说,就是动态添加隐藏底部)
遇到这种bug最莫名其妙了,没有报出自己的代码错误它底层先崩溃,网上搜也没有搜到很明确的错误原因,而且看样子还是很频繁。尽管我解决的方法很简单,但也算是抛砖引玉,给大家排除异常时一个思路吧。根据异常信息,可以分出3种方向:
- IndexOutOfBoundsException 数组越界异常
- Inconsistency detected 发现了不一致
- Invalid view holder 无效的Holder
自己写的代码数组越不越界应该很容易看得出来吧,(反正我这个绝对不是数组越界导致)排除!
然后着重看第二和第三个了,这两个似乎很少见,第三个我猜想应该是 RecyclerView 自己特有的异常判断,是它抛出来的 crash,那么第二个是什么东西…
发现了不一致,什么不一致,就想到了同步锁,因为 adapter 为了保持数据的一致性,在增加和删除数据可能同时操作的情况下,data 数据就必须保持一致性了。这样的猜想就很有可能了,然后看一下自己的代码
getItemViewType 和 onBindViewHolder 这两个方法我也没做什么,就不列出来,我们只看重点,在 onCreateViewHolder 中根据类型判断使用哪种 holder ,很常规的添加不同布局的使用方法了,没什么问题
@Override
public InvoiceHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == NORMAL_ITEM) {
return new InvoiceHolder(LayoutInflater.from(context).inflate(R.layout.item_invoice, parent, false));
} else if (viewType == FOOTER_ITEM){
return new InvoiceCommitHolder(LayoutInflater.from(context).inflate(R.layout.item_invoice_commit, parent, false));
}
return new InvoiceHolder(LayoutInflater.from(context).inflate(R.layout.item_invoice, parent, false));
}
接着是自己定义的方法添加底部,这段代码使用过程中也没有出现什么问题,如果已经有了footerView,就抛异常,没有就添加,运行也很正常,估计也没有问题的。
public void addFooterView(View view) {
if (footerView != null) {
throw new IllegalStateException("footerview has already exists!");
} else {
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
view.setLayoutParams(params);
footerView = view;
notifyItemInserted(getItemCount() - 1);
}
}
//因为底部不算是data数据集的一个数据,固然在 getItemCount 就需要自己判断是否需要 +1
@Override
public int getItemCount() {
int count = mData.size();
if (footerView != null)
count++;
return count;
}
最后,移除底部布局
public void removeFooterView() {
footerView = null;
notifyDataSetChanged();
// notifyItemInserted(getItemCount() - 1);
}
关键就是这里,是用 notifyDataSetChanged(正常) , 而不是用 notifyItemInserted(异常)
至于是为什么,我看源码解释说,当 item 被插入到新位置时通知任何注册了的观察者,item 以前在的位置是目前位置+ 1, 这是一个结构性变化的事件。表示数据集的其他现有的项目仍然被认为是最新的,不会反弹,尽管他们的位置可能会改变。也就是说数据集只更新了制定的那一项,其它不动,然后我让它指定更新的那一 item 却是 null ,所以就出现了问题。
个人水平有限,有问题可以大家探讨。