RecyclerView:检测到不一致.商品位置无效

Kar*_*pka 240 android android-layout screen-rotation android-recyclerview

我们的QA检测到一个错误:当旋转Android设备(Droid Turbo)时,发生以下与RecyclerView相关的崩溃:

java.lang.IndexOutOfBoundsException:检测到不一致.无效的项目位置2(偏移量:2).state:3

对我来说,它看起来像RecyclerView内部的一个内部错误,因为我无法想到这是由我们的代码直接导致的任何方式......

有谁遇到过这个问题?

什么是解决方案?

一个残酷的解决方法可能是在发生异常时捕获异常并从头开始重新创建RecyclverView实例,以避免陷入损坏状态.

但是,如果可能的话,我想更好地理解这个问题(并且可能在其源头修复它),而不是掩盖它.

这个bug很难重现,但是当它发生时它是致命的.

完整的堆栈跟踪:

W/dalvikvm(7546):threadid = 1:线程退出,未捕获异常(组= 0x41987d40)E/AndroidRuntime(7546):致命异常:主E/AndroidRuntime(7546):进程:com.oblong.mezzedroid,PID:7546 E/AndroidRuntime(7546):java.lang.IndexOutOfBoundsException:检测到不一致.无效的项目位置2(偏移量:2).state:3 E/AndroidRuntime(7546):在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):在android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)E/AndroidRuntime(7546):在android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)E/AndroidRuntime(7546):在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):在android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942))E/AndroidRuntime(7546):在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):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546) ):在android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)E/AndroidRuntime(7546):在android.widget.FrameLayout.onLayout(FrameLayout.java:388)E/AndroidRuntime(7546):在android.view .View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.FrameLayout.layoutChildren(FrameLayout) .java:453)E/AndroidRuntime(7546):在android.widget.FrameLayout.onLayout(FrameLayout.java:388)E/AndroidRuntime(7546):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)E/AndroidRuntime(7546):在android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)E/AndroidRuntime( 7546):在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):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)E/AndroidRuntime(7546):在android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)E/AndroidRuntime(7546):在android.widget.LinearLayout. onLayout(LinearLayout.java:1434)E/AndroidRuntime(7546):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java: 4651)E/Android 运行时(7546):在android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)E/AndroidRuntime(7546):在android.widget.FrameLayout.onLayout(FrameLayout.java:388)E/AndroidRuntime(7546):at android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.FrameLayout. layoutChildren(FrameLayout.java:453)E/AndroidRuntime(7546):在android.widget.FrameLayout.onLayout(FrameLayout.java:388)E/AndroidRuntime(7546):在android.view.View.layout(View.java: 14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)E/AndroidRuntime( 7546):在android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)E/AndroidRuntime(7546):在android.widget.LinearLayout.onLayout(LinearLayout.java:1434)E/AndroidRuntime(7546):在android. view.View.layout(View.java:14946)E/Andro idRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)E/AndroidRuntime(7546):at android.widget.FrameLayout.onLayout(FrameLayout.java:388)E/AndroidRuntime(7546):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup. layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)E/AndroidRuntime(7546):在android.widget.LinearLayout.layoutVertical(LinearLayout.java: 1525)E/AndroidRuntime(7546):在android.widget.LinearLayout.onLayout(LinearLayout.java:1434)E/AndroidRuntime(7546):在android.view.View.layout(View.java:14946)E/AndroidRuntime( 7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime(7546):在android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)E/AndroidRuntime(7546):在android. widget.FrameLayout.onLayout(FrameLayout.java:3 88)E/AndroidRuntime(7546):在android.view.View.layout(View.java:14946)E/AndroidRuntime(7546):在android.view.ViewGroup.layout(ViewGroup.java:4651)E/AndroidRuntime( 7546):在android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)E/AndroidRuntime(7546):在android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)E/AndroidRuntime(7546):at andro

小智 182

我有一个(可能)相关的问题 - 使用RecyclerView输入一个活动的新实例,但是使用较小的适配器为我引发了这个崩溃.

RecyclerView.dispatchLayout()在打电话之前可以尝试从废料中提取物品mRecycler.clearOldPositions().结果是它从公共池中拉出位置高于适配器大小的项目.

幸运的是,只有在启用了PredictiveAnimations时才会这样做,因此我的解决方案是继承GridLayoutManager(LinearLayoutManager具有相同的问题和'fix'),并覆盖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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 先生非常感谢您!使用LinearLayoutManager即刻工作,可能节省了我几天的时间. (7认同)
  • 我认为这个人值得我们建造一座雕像以纪念他的宝贵帮助...这是网上记录最严重的问题之一,但似乎有很多开发人员遇到这个问题...我只是想知道*你是怎么找到的?如果PredictiveAnimations为false,可以跳过它,@ KasHunt?因为,堆栈跟踪非常不清楚...... (7认同)
  • 非常感谢.此解决方案适用于LinearLayoutManager. (5认同)
  • 这对我有用,并且禁用预测动画不会导致您一起丢失动画.布拉沃. (4认同)
  • 摆脱这种情况的另一种方法是在RecyclerView上设置新适配器后立即调用notifyDataSetChanged(). (3认同)
  • 谁知道,没有这个黑客怎么解决这个问题?因为notifyDatasetChanged被删除而支持DiffUtil (3认同)
  • 使用 ListAdapter 为我工作,设置 recyclerView.itemAnimator = null (3认同)
  • Android支持库或Google代码上是否有相应的错误提示? (2认同)
  • 我将其添加到我的代码中,它似乎减少了崩溃的数量,但它们并没有完全消失。是否还有其他原因导致我收到“检测到不一致”。无效的项目位置 184`? (2认同)

Mat*_*ejC 77

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

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

  • 我通常不这么说,但谢谢你.我已经尝试了一切来修复这种崩溃,当我在快速排序的列表中移动一堆项目时偶尔发生这种情况.我花了整整一周时间试图解决这个问题.我离开它几个月试图让我的大脑有机会以不同的方式接近它,然后在我的第一次谷歌尝试中发现了这一点.祝福你! (5认同)
  • 这是一个非常繁重的操作,有点破坏了回收视图的目的. (3认同)
  • 如果我使用 MVVM 模型,我必须在哪里编写这段代码? (2认同)

kha*_*ntt 38

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

  • 这根本不是解决方案. (11认同)
  • 在某些情况下,这是要走的路.我有一种情况,我替换了所有的项目,但我对适配器不诚实,只告诉它我插入了一些新项目(notifyItemRangeInserted),没有先告诉它我也删除了项目.然后适配器预计会有更多的项目,然后实际上.如果使用任何适配器的notify方法期望notifyDataSetChanged,例如notifyItemRangeRemoved/Inserted/Updated,则调用者完全有责任告知适配器*确切*更改了什么,或者您可能最终得到这种"不一致状态". (5认同)

Ram*_*rel 15

当用户滚动时适配器中的列表清除时会发生此错误,这使得项目持有者的位置发生变化,在 ui 上的列表和项目之间丢失了引用,错误发生在下一个 “notifyDataSetChanged”请求中。

使固定:

查看您的更新列表方法。如果你做类似的事情

mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur
Run Code Online (Sandbox Code Playgroud)

怎么修。为缓冲区处理创建新的列表对象,然后再次分配给主列表

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();
Run Code Online (Sandbox Code Playgroud)

感谢Nhan Cao的大力帮助 :)


Oda*_*aym 11

我通过mRecycler.setAdapter(itemsAdapter)将所有项目添加到适配器后将延迟延迟解决了这个问题mRecycler.addAll(items)并且工作正常.我不明白为什么我这样做,它来自我看过的图书馆代码,看到那些"错误的顺序",我很确定这是它,如果有人可以确认它解释为什么它是所以?不确定这是否是一个有效的答案

  • 我使用`swapAdapter(adapter,true)`而不是`setAdapter(adapter)`来帮助它. (15认同)

Aal*_*lap 11

我有类似的问题,但不完全一样.在我的情况下,1点我正在清理传递给recyclerview的数组

mObjects.clear();
Run Code Online (Sandbox Code Playgroud)

而不是调用notifyDataSetChanged,因为我不希望recyclerview立即清除视图.我在AsyncTask中重新填充了mObjects数组.


Fat*_*Gee 8

对我来说,添加这行代码后它就起作用了:

mRecyclerView.setItemAnimator(null);
Run Code Online (Sandbox Code Playgroud)

  • 在大多数情况下,这不是一个解决方案,如果您想要动画,您必须重写适配器代码并在通知更改时找到错误 (3认同)

Ana*_*ani 7

我有同样的问题.当我快速滚动并调用API并更新数据时,就会发生这种情况.在尝试所有事情以防止崩溃之后,我找到了解决方案.

mRecyclerView.stopScroll();
Run Code Online (Sandbox Code Playgroud)

它会工作.

  • 这是一种解决方法,而不是修复方法。您正在强迫它停止滚动。糟糕的用户体验 (2认同)

kas*_*iya 6

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

myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});
Run Code Online (Sandbox Code Playgroud)

希望能帮助到你


Erh*_*nis 5

就我而言,我正在更新项目并调用notifyDataSetChanged非 UI 线程。大多数时候它都有效,但是当大量变化快速发生时,它就会崩溃。当我这样做时,基本上

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        changeData();
        notifyDataSetChanged();
    }
});
Run Code Online (Sandbox Code Playgroud)

然后它就停止崩溃了。


Mig*_*iel 5

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

代替:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
     private List<MyItem> mItems;  

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}
Run Code Online (Sandbox Code Playgroud)

我做了:

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}
Run Code Online (Sandbox Code Playgroud)


Rez*_*eza 5

我在recyclerView上遇到了同样的问题,所以我只是在清除列表后立即通知适配器有关数据集更改的信息。

mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();
Run Code Online (Sandbox Code Playgroud)

  • 这个简单的错误让我浪费了很多时间,非常感谢! (2认同)

Ram*_*dav 5

这是唯一对我有用的解决方案,即使尝试了上面的许多解决方案。

1.) 实用化

CustomAdapter scrollStockAdapter = 
         new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);
Run Code Online (Sandbox Code Playgroud)

2.)将此方法写入适配器中

public void updateList(List<StockListModel> list) {
    stockListModels.clear();
    stockListModels.addAll(list);
    notifyDataSetChanged();
}
Run Code Online (Sandbox Code Playgroud)

stockListModels -> 此列表是您在适配器中使用的列表。