RecyclerView:检测到不一致。 无效的项目位置

我们的QA发现了一个bug:在Android设备(Droid Turbo)旋转时,发生了以下与RecyclerView相关的崩溃:

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3 

对我来说,它看起来像RecyclerView内部的错误,因为我不能想到这是由我们的代码直接造成的任何方式…

有没有人遇到过这个问题?

什么是解决scheme?

一个残酷的解决方法可能是在发生exception时捕获exception,并从头开始重新创buildRecyclverView实例,以避免出现损坏状态。

但是,如果可能的话,我想更好地理解这个问题(也许可以从源头上解决问题),而不是掩盖它。

该错误不容易重现,但发生时是致命的。

完整的堆栈跟踪:

  >W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40) >E/AndroidRuntime( 7546): FATAL EXCEPTION: main >E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546 >E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3 >E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382) >E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340) >E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810) >E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306) >E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269) >E/AndroidRuntime( 7546): at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523) >E/AndroidRuntime( 7546): at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179) >E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942) >E/AndroidRuntime( 7546): at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237) >E/AndroidRuntime( 7546): at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) >E/AndroidRuntime( 7546): at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525) >E/AndroidRuntime( 7546): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453) >E/AndroidRuntime( 7546): at android.widget.FrameLayout.onLayout(FrameLayout.java:388) >E/AndroidRuntime( 7546): at android.view.View.layout(View.java:14946) >E/AndroidRuntime( 7546): at android.view.ViewGroup.layout(ViewGroup.java:4651) >E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132) >E/AndroidRuntime( 7546): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872) >E/AndroidRuntime( 7546): at andro 

我有一个(可能)相关的问题 – 用RecyclerViewinput一个新的活动实例,但是一个较小的适配器正在为我引发这个崩溃。

在调用mRecycler.clearOldPositions()之前, RecyclerView.dispatchLayout()可以尝试从报废中提取项目。 其结果是,它是从普通池中拉出的物品比适配器的尺寸高。

幸运的是,如果PredictiveAnimations被启用,它只会这样做,所以我的解决scheme是子类GridLayoutManager(LinearLayoutManager有相同的问题和'修复'),并重写supportsPredictiveItemAnimations()返回false:

 /** * No Predictive Animations GridLayoutManager */ private static class NpaGridLayoutManager extends GridLayoutManager { /** * Disable predictive animations. There is a bug in RecyclerView which causes views that * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the * adapter size has decreased since the ViewHolder was recycled. */ @Override public boolean supportsPredictiveItemAnimations() { return false; } public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } public NpaGridLayoutManager(Context context, int spanCount) { super(context, spanCount); } public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) { super(context, spanCount, orientation, reverseLayout); } } 

在我的情况(删除/插入我的数据结构中的数据)我需要清除回收池,然后通知数据集更改!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();

在这种情况下,使用notifyDataSetChanged()而不是notifyItem...

我通过延迟mRecycler.setAdapter(itemsAdapter)解决了这个问题,直到使用mRecycler.addAll(items)将所有项目添加到适配器后,它才起作用。 不知道为什么我这样做,从图书馆的代码,我看了过来,看到这些行“错误的顺序”,我敢肯定,这是它,但如果有人可以确认它,请解释为什么它是所以? 不知道这是否是一个有效的答案

我有类似的问题,但不完全一样。 在我的情况下,在1点,我正在清除传递给回收站的数组

 mObjects.clear(); 

而不是调用notifyDataSetChanged,因为我不想让recyclerview立即清除视图。 我正在重新填充AsObjectTask中的mObjects数组。

我有同样的问题。当我快速滚动并调用API和更新数据时发生。尝试所有事情以防止崩溃后,我find了解决办法。

 mRecyclerView.stopScroll(); 

它会工作。

这也可能与多次同时设置适配器有关。 我有一个被同时触发5-6次的callback方法,我在callback中设置了适配器,所以RecycledViewPool不能同时处理所有这些数据。 这是一个很难的机会,但你最好还是检查一下。

我修改我的Adapter实现使用items数组的副本而不是引用后,我的问题就消失了。 每次在RecyclerView显示新项目时,都会调用setItems()方法。

代替:

 private class MyAdapter extends RecyclerView.Adapter<ItemHolder> { private List<MyItem> mItems; (....) void setItems(List<MyItem> items) { mItems = items; } } 

我做了:

 void setItems(List<MyItem> items) { mItems = new ArrayList<>(items); } 

使用

 notifyDataSetChanged() 

代替

 notifyItemRangeInserted(0, YourArrayList.size()) 

在这种情况下。

我遇到了类似的问题,只是想出来。 我为一个testing用例硬编码了一些例子,但是并不确保他们每个都返回一个唯一的ID,导致下面的崩溃。 修复ID解决了问题,希望这可以帮助别人!

为了解决这个问题,只需在更新回收视图之前调用带有空列表的notifyDataSetChanged()。

例如

 //Method for refresh recycle view if (!hcpArray.isEmpty()) 

hcpArray.clear(); //更新回收视图列表

 adapter.notifyDataSetChanged(); 

我解决了这个问题,当它获得新的数据时,逐个添加项目。 我在适配器中使用这个函数。

 public void add(Data item) { if(!params.contains(item){ params.add(item); notifyItemInserted(getItemCount() - 1); } } } 

当你尝试清除你的列表,如果你要清除你的数据列表,特别是当你使用pull来刷新的时候,可能会发生这个问题。尝试使用布尔标志,将它初始化为false,并且在OnRefresh方法中使其成为true,清除你的dataList如果在向其添加新数据之前标志为真,那么将其设为假。

你的代码可能是这样的

  private boolean pullToRefreshFlag = false ; private ArrayList<your object> dataList ; private Adapter adapter ; public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{ private void requestUpdateList() { if (pullToRefresh) { dataList.clear pullToRefreshFlag = false; } dataList.addAll(your data); adapter.notifyDataSetChanged; @Override OnRefresh() { PullToRefreshFlag = true reqUpdateList() ; } } 

我发现设置mRecycler.setLayoutFrozen(true); 在swipeContainer的onRefresh方法中。

为我解决了这个问题。

 swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { orderlistRecycler.setLayoutFrozen(true); loadData(false); } }); 

我以前也有同样的问题。 最后find了一个解决方法

我所做的是通知适配器该项目已被删除,然后通知适配器数据集范围已更改

  public void setData(List<Data> dataList) { if (this.dataList.size() > 0) { notifyItemRangeRemoved(0, dataList.size()); this.dataList.clear(); } this.dataList.addAll(dataList) notifyItemRangeChanged(0, dataList.size()); } 

我面对同样的情况。 在清除collections之前,通过添加代码解决了这个问题。

mRecyclerView.getRecycledViewPool().clear();

这是一个相当讨厌的错误。

为了处理我的项目点击,我使用了RecyclerView.OnItemTouchListener类似于这个问题中find的解决scheme的实现。

刷新RecyclerView的数据源并点击一个项目多次后,这个IndexOutOfBoundsException将会崩溃我的应用程序。 当一个项目被点击时, RecyclerView会在内部查找正确的底层视图并将其返回。 检查出源代码,我看到有一些TasksThreads计划。 简而言之,基本上这只是一个非法的状态,两个数据源混合在一起,而不是同步,整个事情都是疯狂的。

基于此,我删除了RecyclerView.OnItemTouchListener实现,并简单地在AdapterViewHolder上单击自己:

 public void onBindViewHolder (final BaseContentView holder, final int position) { holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick (View view) { // do whatever you like here } }); } 

这可能不是最好的解决scheme,但现在是免费的。希望这会为您节省一些时间:)。

在我的情况下,我正在更新项目,并在非UI线程中调用notifyDataSetChanged 。 大多数情况下,它的工作,但是当很多变化发生很快,它会崩溃。 当我这样做的时候,基本上是这样

 activity.runOnUiThread(new Runnable() { @Override public void run() { changeData(); notifyDataSetChanged(); } }); 

那么它停止崩溃。

我正在后台Thread更改RecyclerView数据。 我得到了和OP一样的Exception 。 我在更改数据后添加了这个:

 myRecyclerView.post(new Runnable() { @Override public void run() { myRecyclerAdapter.notifyDataSetChanged(); } }); 

希望能帮助到你

在我的情况下,我试图改变我的适配器的内容在后台线程上,但在主/ UI线程上调用notify …。

这实际上是不可能的! 通知被强制为主线程的原因是recyclerview要求您在主线程上编辑您的后备适配器,即使在同一个调用堆栈上。

为了解决这个问题,确保你的适配器的每一个操作,以及每个通知…调用是在ui /主线程上进行的

我曾经有过这样的错误:

原因:我试图从一个asynchronous任务更新回收站视图,同时尝试获取旧的删除viewHolders;

代码:我按下button生成数据,逻辑如下

  1. 清除回收站视图中的最后一个项目
  2. 调用asynchronous任务来生成数据
  3. OnPostExecute更新Recycler视图和NotifyDataSetChanged

问题:每当我滚动快速生成我的数据之前,我得到

检测到不一致 无效的视图持有者适配器positionViewHolder java.lang.IndexOutOfBoundsException:检测到不一致。 无效的项目位置20(偏移:2)。状态:3

解决scheme:不是在生成我的数据之前清除RecyclerView,而是保留它,然后将其replace为New Data,Call NotifyDatasetChanged,如下所示;

  @Override protected void onPostExecute(List<Objects> o) { super.onPostExecute(o); recyclerViewAdapter.setList(o); mProgressBar.setVisibility(View.GONE); mRecyclerView.setVisibility(View.VISIBLE); } 

在我的情况下,我刚刚删除行与setHasStableIds(true);

林特给了我一个不一致的build议:我写(onBindViewHolder()):

 pholder.mRlayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doStuff(position); } }); 

必须由以下内容取代:

 pholder.mRlayout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doStuff(pholder.getAdapterPosition()); } }); 

在代码中运行这两个代码,然后运行Lint进行完整的说明!

只要在通知之前删除布局pipe理器的所有视图。 喜欢:

 myLayoutmanager.removeAllViews();