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

Android RecyclerView:检测到不一致。无效的项目位置,android,android-layout,android-recyclerview,screen-rotation,Android,Android Layout,Android Recyclerview,Screen Rotation,我们的QA检测到一个bug:当旋转Android设备(Droid Turbo)时,发生了以下与RecyclerView相关的崩溃: java.lang.IndexOutOfBoundsException: 检测到不一致。无效的项目位置2(偏移量:2)。状态:3 对我来说,这看起来像是RecyclerView内部的一个内部错误,因为我想不出这是由我们的代码直接导致的 有人遇到过这个问题吗 解决办法是什么 一个残酷的解决方法可能是在异常发生时捕获异常并从头开始重新创建RecyclverView实例,

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

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

对我来说,这看起来像是RecyclerView内部的一个内部错误,因为我想不出这是由我们的代码直接导致的

有人遇到过这个问题吗

解决办法是什么

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

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

这个错误不容易复制,但一旦发生就致命了

完整堆栈跟踪:

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

我通过将
mRecycler.setAdapter(itemsAdapter)
延迟到使用
mRecycler.addAll(items)
将所有项添加到适配器之后,才解决了这个问题,它起了作用。不知道我为什么要这么做,我是从一个库的代码中看出来的,我看到这些行的顺序是“错误的”,我很确定就是这样,如果有人能证实的话,请解释一下为什么会这样?不确定这是否是一个有效的答案,即使在本例中使用
notifyDataSetChanged()
而不是
notifyItem…

我有一个(可能)相关的问题-使用RecyclerView输入活动的新实例,但使用较小的适配器会触发此崩溃

RecyclerView.dispatchLayout()
可以在调用
mRecycler.clearOldPositions()
之前尝试从废料中提取项目。结果是,它正在从公共池中提取位置高于适配器大小的项

幸运的是,只有启用了
PredictiveAnimations
时,它才会这样做,因此我的解决方案是将
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);
    }
}

我也有类似的问题,但不完全一样。在我的例子中,在1点,我正在清除传递给recyclerview的数组

mObjects.clear();
并且没有调用notifyDataSetChanged,因为我不希望recyclerview立即清除视图。我正在AsyncTask中重新填充mObjects数组。

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

mRecyclerView.getRecycledViewPool().clear();

mAdapter.notifyDataSetChanged()

我也有同样的问题。这是在我快速滚动、调用API和更新数据时发生的。在尝试了所有防止崩溃的措施后,我找到了解决方案

mRecyclerView.stopScroll();

它会起作用。

当您尝试清除列表时,可能会出现此问题,如果您要清除数据列表,尤其是在使用pull刷新时,请尝试使用布尔标志,将其初始化为false,并在OnRefresh方法中使其为true,在将新数据添加到dataList之前,如果标志为true,则清除dataList,然后使其为false

您的代码可能是这样的

 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() ; 
 }

}
private boolean pullToRefreshFlag=false;
私有ArrayList数据列表;
专用适配器;
公共类myClass扩展片段实现SwipeRefreshLayout.OnRefreshListener{
私有void requestUpdateList(){
if(pullToRefresh){
dataList.clear
pullToRefreshFlag=false;
}
dataList.addAll(您的数据);
adapter.notifyDataSetChanged;
@凌驾
OnRefresh(){
PullToRefreshFlag=true
reqUpdateList();
}
}

这也可能与同时多次设置适配器有关。我有一个回调方法,该方法在同一时间被触发5-6次,我在该回调中设置适配器,因此RecycledViewPool无法同时处理所有这些数据。这是一个很小的机会,但你最好还是检查一下。

I以前也有同样的问题。最终找到了解决方法

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

 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());

 }
public void setData(列表数据列表){
if(this.dataList.size()>0){
notifyItemRangeRemoved(0,dataList.size());
this.dataList.clear();
}
this.dataList.addAll(dataList)
notifyItemRangeChanged(0,dataList.size());
}

我也遇到过同样的情况。 在你清理你的收藏之前,通过添加代码解决了这个问题


mRecyclerView.getRecycledViewPool().clear();

在我的例子中,我在一个非UI线程中更新项目并调用
notifyDataSetChanged
。大部分时间它工作正常,但当大量更改很快发生时,它就会崩溃。相反,当我这样做时,它基本上是崩溃的

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

然后它停止崩溃。

在我修改了
适配器
实现以使用项数组的副本而不是引用之后,我的问题就消失了。每当我们在
回收视图中显示新项时,就会调用
setItems()
方法

而不是:

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

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}
私有类MyAdapter扩展了RecyclerView.Adapter{
私人名单;
(....)
作废集合项(列表项){
mItems=项目;
}
}
我做到了:

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}
void集合项(列表项){
mItems=新阵列列表(项目);
}
使用

反而

notifyItemRangeInserted(0, YourArrayList.size())

在这种情况下。

我正在后台
线程
中更改
回收视图
的数据。我得到了与OP相同的
异常情况
。我在更改数据后添加了以下内容:

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

希望它能有所帮助

我遇到了一个类似的问题,刚刚解决了。我为一个测试用例硬编码了几个示例,但没有确保每个示例都返回一个唯一的ID,从而导致下面的崩溃。修复
myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});
//Method for refresh recycle view

    if (!hcpArray.isEmpty())
adapter.notifyDataSetChanged();
       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }
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());
                    }
                });
myLayoutmanager.removeAllViews();
// Setup refresh listener which triggers new data loading
        swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {

                AsyncTask<String,Void,String> task = new get_listings();
                task.execute(); // clear listing inside onPostExecute

            }
        });
        swipeContainer.setRefreshing(false);
        //TODO : This is very crucial , You need to clear before populating new items 
        listings.clear();
mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();
CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);
public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}
mRecyclerView.setItemAnimator(null);
mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur
List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();
adapter.submitList(null)
adapter.submitList(someDataList)
adapter.addItem(item)
var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}
view.recycler_view.post { adapter.addItem(item) }
public class NoCrashLinearLayoutManager extends LinearLayoutManager {

    public NoCrashLinearLayoutManager(Context context) {
        super(context);
    }

    public NoCrashLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public NoCrashLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e){
            e.printStackTrace();
        }
    }
}