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

java.lang.IndexOutOfBoundsException: 不一致。无效项位置2(偏移量:2).state: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

当前回答

当你尝试清除你的列表时,这个问题可能会发生,如果你要清除你的数据列表,特别是当你使用拉刷新尝试使用一个布尔标志,初始化它为假,在OnRefresh方法使它为真,清除你的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() ; 
 }

}

其他回答

我通过延迟mrecycle . setadapter (itemsAdapter)解决了这个问题,直到添加了所有的项目到适配器与mrecycle . addall(项目),它工作。不知道我为什么这么做,这是从一个库的代码,我看了看,看到这些行在“错误的顺序”,我很确定这是它,如果有人能证实它,请解释为什么是这样?甚至不确定这是否是一个有效的答案

这个异常在API 19、21上引发(但不是新的)。在Kotlin协程中,我加载了数据(在后台线程),并在UI线程中添加并显示它们:

adapter.addItem(item)

适配器:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}

由于某些原因,Android渲染速度不够快或其他原因,所以,我更新了RecyclerView的post方法中的列表(添加,删除,更新项目的事件):

view.recycler_view.post { adapter.addItem(item) }

此异常类似于“无法在滚动回调中调用此方法”。滚动回调可能在测量和布局传递期间运行,其中您不能更改theRecyclerView数据。任何可能改变RecyclerView结构或适配器内容的方法调用都应该延迟到下一帧。": Recyclerview -不能在滚动回调中调用此方法。

在我的例子中,我使用的是DiffUtil。calculateDiff用于回收视图项,它在更新项时具有竞态条件。我在后台线程池中计算数据差异,并在UI线程中更新。如果有2个数据,并且第一个数据被设置为第二个差分计算和设置的时间之间的适配器,则会发生崩溃。

例如,假设我们有原始项目列表A、新数据B和新数据C:

B-diff =已计算(A, B) C-diff =已计算(A, C) 集(B-diff) 集(C-diff)

C-diff实际上是不正确的,因为A已经更新,所以如果物品计数减少,recyclerview将在无效位置上动画。不只是崩溃,项目可能根本没有更新(或不必要的更新)由于不正确的差异索引。

更改为AsyncListDiffer解决了我的问题,因为如果第二个数据来得太早,它会删除第一个数据。

参考(它描述了问题,但给出了另一种解决方案(按顺序更新数据)):https://jonfhancock.com/get-threading-right-with-diffutil-423378e126d2

我最近在使用新的Android架构组件时遇到了这种讨厌的堆栈跟踪。从本质上讲,我在ViewModel中有一个项目列表,由我的Fragment使用LiveData观察。当ViewModel发布数据的新值时,Fragment更新适配器,传入这些新的数据元素并通知适配器已经发生了变化。

不幸的是,当将新的数据元素传递给适配器时,我没有考虑到ViewModel和adapter都指向同一个对象引用!这意味着,如果我在ViewModel中更新数据并调用postValue(),就会有一个非常小的窗口,可以在其中更新数据,而适配器还没有收到通知!

我的修复是实例化一个新的副本的元素时,传递到适配器:

mList = new ArrayList<>(passedList);

有了这个超级简单的修复,您可以确保您的适配器数据不会改变,直到您的适配器被通知。

如果notifyItemChanged(int position), notifyItemInserted(int position) 当您想要更新项时使用notifyItemChanged,而当您想要添加项时使用notifyItemInserted时,会出现此问题