使用RecyclerView遇到的一些问题 Inconsistency detected
来源:互联网 发布:单词社交网络高级版pdf 编辑:程序博客网 时间:2024/05/17 10:06
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{424b7690 position=7 id=-1, oldPos=8,pLpos:8 scrap tmpDetached no parent} at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4349)
以及
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
这个问题在google官方话题单有提到,地址为
ISSUE 77846
其中,项目的pm 有提及如下两点说明:
1.ListView and RecyclerView are different
ListView and RecyclerView are different. RecyclerView is designed to work with different components and makes certain promises. For the error above, exception happens when LayoutManager tries to get a View for a position. A count is already provided to the LayoutManager at the beginning of the layout, this is a promise and guaranteed not to change until the layout is complete. RecyclerView cannot say “you have 6 items” and when LayoutManager asks for item at position 5, return null. LayoutManager might have done its own calculations based on that count, doing so may leave its state unstable. On the other hand, ListView has full control, thus it can forgive these things. Besides that, ListView does not do anything clever about the adapter contents whereas RecyclerView does a lot to support animations. The two are fairly different. To be honest, if you do not dispatch detailed notify events, there is little to no benefit on moving to RecyclerView. If you dispatch them, it will both help UX and performance. (e.g. avoiding unnecessary rebinds)
RecyclerView throws an exception because problem happens due to a developer error and should be fixed. If it is a RecyclerView, we have to fix it. In both cases, for a consistent and stable API, forgiving developer errors (both ends) is not a sustainable solution.
About adapter count, that getItemCount is one API I regret leaving public (was an old API, had to be kept for some backward compatibility).
LayoutManagers are expected to get item count from the State. If you check framework layout managers, all work w/ state. There is a strict abstraction between the Adapter and LayoutManager (due to animations). Even for notify events, RecyclerVIew re-writes them (in a consistent way) to suit them for two pass animations. There is a lot going on there, hard to explain here. (see docs: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.State.html#getItemCount())
RecyclerView also provides an API to convert layout positions to adapter positions if necessary.
convertPreLayoutPositionToPostLayout : https://developer.android.com/reference/android/support/v7/widget/RecyclerView.Recycler.html#convertPreLayoutPositionToPostLayout(int)
The trigger for the bug might be events while RV is detached, some Runnable may not be running due to View being detached. Some info / logs would be very helpful so that i can create a test case and fix it.
Thanks.
2.let RV know about it
Great to hear that issue is fixed. I did not understand why you are calling notifyInserted w/o inserting them. It will definitely create a problem. This explains why RV expects to have more items in the adapter.
Only call these events right after you change the data. (has to be in the same call stack ~ main looper loop~)
Notify events are handled asynchronously. So you can call as many notify events as you want and RV will handle all of them in the next layout pass.It batches them etc. You just need to guarantee that all of them are consistent with each other. That is, in every step you change adapter, you should let RV know about it. Your events should be consistent. For example, if you want to remove first two elements 1 by one, you should call:
mData.removeItemAt(0);
notifyItemRemoved(0);
mData.removeItemAt(0);
notifyItemRemoved(0);
A common mistake would be to think that you need to call notifyItemRemoved(0); then notifyItemRemoved(1);. This is NOT true as RV knows items will shift if first item is removed. This is also consistent w/ what you would do while handling a list.
This is the simplest way to get them right. Technically, you can let RV know right after you update the backing data, as long as you do it in the same call stack.
e.g.
mData.removeItemAt(0);
mData.removeItemAt(0);
notifyItemRangeRemoved(0, 2); //2 items, starting from 0.
So your code probably works fine if notifyDataSetChanged arrives before the next layout calculation but fails otherwise.
When you call notifyDataSetChanged, you void all previous notify events (in that frame). Don’t call notifyDataSetChanged if you don’t have to.
Good luck and thanks for the update, I’m closing the issue.
第一点,阐述了RV(RecyclerView)和ListView的区别。如果不涉及动画等特效,PM是建议大家使用ListView的。RecyclerView使用的关键在于“动态”绑定了数据,通过LayoutManager来进行数据的相关界面展示。也就是说,如果我当前RV的数据变化不当时(即开发者使用不当时候)会造成RV的错误。PM顺便吐槽了下,出了问题也不能只怪我们开发组,有时候是你开发者的问题(囧–!)。
我的项目需求是实现一个类似Gallery的相册。在使用RV时,高度并不能wrap_content,为此,我复写了LayoutManager。因此,在
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {....}
方法中需要测量出RV的子控件的宽高进行界面的适配。通过
Logger.d("state:" + state.toString());
可以看到state包含如下数据
state:State{mTargetPosition=-1, mPreLayoutHolderMap={}, mPostLayoutHolderMap={}, mData=null, mItemCount=6, mPreviousLayoutItemCount=6, mDeletedInvisibleItemCountSincePreviousLayout=0, mStructureChanged=true, mInPreLayout=false, mRunSimpleAnimations=false, mRunPredictiveAnimations=false}
但遗憾的是state并没有完全提供这些数据的get方法。
如PM所说,其实state已经包含了我们需要的一系列的信息,其实可以避免使用getItemCount()方法来获取当前的子数据数量的。
第二点,提出了解决遇到这些问题的关键原因。
在进行数据移除和数据增加时,务必要保证RVAdapter中的数据和移除的数据保持一致!
此外,不建议使用notifyDataSetChanged 方法,因为这会让美好的效果通通木有了。
个人认为,如果使用了notifyDataSetChanged ,那还不如使用listview了。
我的项目中遇到的问题是,多选删除后,会报Inconsistency detected的错误,结合在进行数据移除和数据增加时,务必要保证RVAdapter中的数据和移除的数据保持一致!来进行排查,发现了问题所在:从服务器获取到删除图片成功的返回值后,先进行了data的清除,然后在主线程里面又结合notifyItemRemoved重新删除了一次!
我项目中报过好几次Inconsistency detected错误,因此,在遇到相关问题时,应该第一时间切记数据源和RV变动数据的同步。当然,这个bug也可能是其他原因导致的,如果有,我再更新上来。
- 使用RecyclerView遇到的一些问题 Inconsistency detected
- 使用RecyclerView遇到的 Inconsistency detected
- 当RecyclerView遇到Inconsistency detected崩溃时
- RecyclerView的bug——Inconsistency detected
- RecyclerView 的IndexOutOfBoundsException: Inconsistency detected异常解决办法
- RecyclerView 数据不一致 Inconsistency detected
- 使用Recyclerview控件遇到的一些问题
- 关于使用RecyclerView遇到的一些问题集合
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解决方案
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解决方案
- RecyclerView系列(4)—XRexyclerView的坑,java.lang.IndexOutOfBoundsException: Inconsistency detected
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter的解决方案
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid item position 解决方案
- RecyclerView:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid item position …
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid item position
- RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid item position
- 成功解决RecyclerView Bug:IndexOutOfBoundsException: Inconsistency detected. Invalid item position
- 用Alt和小键盘输入字符或汉字 可以输入特殊字符
- mac 截屏快捷键
- RESTful风格的优势是什么
- Android菜单,仿人人客户端侧滑效果,史上最简单的侧滑实现
- 前端性能优化指南
- 使用RecyclerView遇到的一些问题 Inconsistency detected
- 基于openfire+asmack+spark修改的即时通讯
- 二叉树的四种遍历方式(非递归和递归)
- lua闭包理解实例
- SQL查询语句
- 快速排序算法:非递归
- 最全设计模式详解
- no jacob.dll in java.library.path解决办法
- redis清空数据