- 点击跳转=>Unity3D特效百例
- 点击跳转=>案例项目实战源码
- 点击跳转=>游戏脚本-辅助自动化
- 点击跳转=>Android控件全解手册
- 点击跳转=>Scratch编程案例
- 点击跳转=>软考全系列
👉关于作者
专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎底部卡片私我,交流让学习不再孤单。
👉实践过程
😜问题
近来项目里,RecyclerView 更新数据偶尔出现 crash,错误日志如下:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{2064e5c6 position=2 id=-1, oldPos=2, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4505)at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4636)at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4617)at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1994)at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1390)at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1353)at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574)at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:2979)at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2904).....
这类问题出现的原因是数据变化和更新不同步造成的,不如你 clear 了,然后又等接口请求完毕 add 了,只进行了一次 adapter 更新,再比如你在滑动的过程中,数据源被你的某个业务逻辑改变了数据。
总之就是突出一个思想:数据的任何一次变动没有及时进行 adapter 的更新造成的。
遇到这类错误 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder,通过查阅 Stackoverflow 和资料,总结了如下情况和解决方法:
😜解决
方法一
自己写一个继承 LinearLayoutManager 的包装类,在 onLayoutChildren() 方法里 try-catch 捕获该异常。
@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() 做些说明:
使用该方法的更新内部数据集,没有默认的动画效果,同时更新数据的效率页不如上面的方法,官方不推荐使用这种方式更新数据集。
Don’t call notifyDataSetChanged if you don’t have to.
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());}
}
修复分析:
每一次对外部数据集做改动时,都需要紧接着主动对外部数据集和内部数据集做一次同步操作,动画效果也得到了保持(使用方法三,动画效果缺失)
方法三:
使用 notifyDataSetChanged 同步外部数据集和内部数据集。该方法简单,但是失去了动画效果,并且更新数据的性能低。
use notifyDataSetChanged() will avoid this crash, but it will kill Animation and Performance.
另外,如果对外部数据集做了二次以上的操作,却只调用notifyDataSetChanged同步一次,也很有可能会报上文所示的错误。
👉其他
📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。
温馨提示:点击下方卡片获取更多意想不到的资源。