RecyclerView:IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter
2015-11-30 14:17
531 查看
使用RecyclerView时,在动态添加/删除数据时,很有可能会出现下面的错误:
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)官方提出过如下内容:(注意红色字体部分)
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 theLayoutManager 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 havedone 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 RecyclerViewdoes 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. avoidingunnecessary 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-writesthem (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 everystep 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.不想翻译,或看不懂也没关系,注意红色字体部分就行了。其实就是说,list数据的添加/删除 要与adapter的添加/删除 要是同步的。不要 list先添加一条数据,然后还没等adapter也添加时,list又添加了另一条数据。相关文章推荐
- SourceProvider.getJniDirectories
- Trac 中文语言安装
- 软件 bug 的生命周期
- Firefox2中输入框丢失光标bug的解决方法
- for命令的一些bug分析
- 修正IE下使用CSS属性overflow的bug
- 解决IE6 3像素Bug的css写法
- 跟我学习JScript的Bug与内存管理
- JS注释所产生的bug 即使注释也会执行
- IE本地存储userdata的一个bug说明
- IE在DOM操作有表单控件时的bug
- ie 处理 gif动画 的onload 事件的一个 bug
- IIS6 安全性存在超级BUG,快来看
- Android生存指南之:解Bug策略与思路问题的详解
- shell脚本中case条件控制语句的一个bug分析
- 关于在IE下的一个安全BUG --可用于跟踪用户的系统鼠标位置
- jQuery ajax BUG:object doesn't support this property or method
- ShareSDK造成App崩溃的一个BUG原因分析以及Fix方法
- 关于c语言的一个小bug详解
- JavaScript编程中容易出BUG的几点小知识