RecyclerView 自带删除Item方法notifyItemRemoved(position);频繁点击删除时,突然抛出越界异常显示 position = -1 ;
java.lang.ArrayIndexOutOfBoundsException: length=10; index=-1
at java.util.ArrayList.get(ArrayList.java:310)
WTF???活见鬼!
查看getAdapterPosition()源码:
public final int getAdapterPosition() {
return this.mOwnerRecyclerView == null ? -1 : this.mOwnerRecyclerView.getAdapterPositionFor(this);
}
int getAdapterPositionFor(RecyclerView.ViewHolder viewHolder) {
return !viewHolder.hasAnyOfTheFlags(524) && viewHolder.isBound() ? this.mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition) : -1;
}
1
2
3
4
5
6
7
8
从getAdapterPositionFor(ViewHolder viewHolder)看不出什么信息,切换低版本源码查看:
int getAdapterPositionFor(ViewHolder viewHolder) {
if (viewHolder.hasAnyOfTheFlags( ViewHolder.FLAG_INVALID |
ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
|| !viewHolder.isBound()) {
return RecyclerView.NO_POSITION;
}
return mAdapterHelper.applyPendingUpdatesToPosition(viewHolder.mPosition);
}
1
2
3
4
5
6
7
8
当ViewHolder处于FLAG_REMOVED 等状态时会返回NO_POSITION。
我们看一下官方文档解释:
Returns the Adapter position of the item represented by this ViewHolder.
Note that this might be different than the getLayoutPosition() if there are pending adapter updates but a new layout pass has not happened yet.
RecyclerView does not handle any adapter updates until the next layout traversal. This may create temporary inconsistencies between what user sees on the screen and what adapter contents have. This inconsistency is not important since it will be less than 16ms but it might be a problem if you want to use ViewHolder position to access the adapter. Sometimes, you may need to get the exact adapter position to do some actions in response to user events. In that case, you should use this method which will calculate the Adapter position of the ViewHolder.
Note that if you’ve called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION.
The adapter position of the item if it still exists in the adapter. NO_POSITION if item has been removed from the adapter, notifyDataSetChanged() has been called after the last layout pass or the ViewHolder has already been recycled.
其中Note that if you’ve called notifyDataSetChanged(), until the next layout pass, the return value of this method will be NO_POSITION。解释了返回-1的原因,大意是:notify未完成时,调用getAdapterPosition就会返回NO_POSITION(-1)。
adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样。
另外stackoverflow上有答案还提到, 在notifyDataSetChanged之后并不能马上获取Adapter中的position, 要等布局结束之后才能获取到.而对于Layout的position, 在notifyItemInserted之后, Layout不能马上获取到新的position, 因为布局还没更新(需要<16ms的时间刷新视图), 所以只能获取到旧的, 但是Adapter中的position就可以马上获取到最新的position。
但是这都不能解决返回-1突发的crash,我们怎么做?
方法一:在ViewHolder中持有数据源Bean。 通过bean在list中的位置来获取position
方法二:在使用position地方判断 if(position<0) return;