web-dev-qa-db-ja.com

RecyclerView:不一致が検出されました。無効なアイテムの位置

QAがバグを検出しました: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
228
KarolDepka

私は(おそらく)関連する問題を抱えていました - 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);
    }
}
171
Kas Hunt

私の場合(データ構造からデータを削除/挿入する)、私はリサイクルプールをクリアしてからデータセットが変更されたことを通知する必要がありました!

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

70
MatejC

この場合はnotifyItem...の代わりにnotifyDataSetChanged()を使用してください。

39
khaintt

私はmRecycler.setAdapter(itemsAdapter)を使ってアダプタにすべての項目を追加した後までmRecycler.addAll(items)を遅らせることでこれを解決しました、そしてそれはうまくいきました。私が最初にそれをしたのは、私が見たライブラリのコードから "間違った順序"でそれらの行を見たのがわかりませんでした。そう?これが正当な答えであるかどうかさえわからない

11
Odaym

私は似たような問題を抱えていましたが、まったく同じというわけではありません。私の場合は1点で、recyclerviewに渡された配列をクリアしていました

mObjects.clear();

リサイクルデータをすぐにクリアしたくないため、notifyDataSetChangedを呼び出さないでください。 AsyncTaskでmObjects配列を再入力していました。

11
Aalap

私は同じ問題を抱えています。それは私が速くスクロールしてAPIを呼び出してデータを更新していたときに起こりました。

mRecyclerView.stopScroll();

それが動作します。

6
Anand Savjani

このエラーは、ユーザーがアイテムホルダーの位置を変更するときにスクロールすると、アダプター内のリストがクリアされ、リストとアイテムの間の参照が失われ、次の "notifyDataSetChanged"要求でエラーが発生します。

修正:

更新リストの方法を確認してください。あなたが好きなことをしたら

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

===> Error occur

直し方。バッファ処理用の新しいリストオブジェクトを作成し、その後メインリストに再度割り当てます。

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();

Nhan Cao さん、ありがとうございます。

4
Ramesh Pokharel

つかいます

notifyDataSetChanged()

代わりに

notifyItemRangeInserted(0, YourArrayList.size())

この場合。

4
Pankaj Talaviya

この問題を解決するには、リサイクルビューを更新する前に、空のリストを指定してnotifyDataSetChanged()を呼び出すだけです。

例えば

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear(); //更新リサイクルビューのリスト

adapter.notifyDataSetChanged();
3
EKN

RecyclerViewでも同じ問題がありましたので、リストがクリアされた直後にデータセットの変更についてアダプタに通知しました。

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

mList.addAll(newData);
mAdapter.notifyDataSetChanged();
3
Reza

参照ではなくitems配列のコピーを使用するようにAdapterの実装を変更した後、私の問題は解決しました。 setItems()メソッドは、RecyclerViewに表示する新しい項目があるたびに呼び出されます。

の代わりに:

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

私の場合は、アイテムを更新し、非UIスレッドでnotifyDataSetChangedを呼び出していました。ほとんどの場合うまくいきましたが、多くの変更がすぐに起こるとクラッシュしました。私がしたとき、代わりに、基本的に

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

それからそれはクラッシュしなくなりました。

2
Erhannis

RecyclerViewのデータをバックグラウンドThreadで変更しています。私はOPと同じExceptionを手に入れました。データを変更した後にこれを追加しました。

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

それが役に立てば幸い

2

あなたはOnPostExecute()上のあなたのリストをクリアする必要があるだけで、Pull to Refreshをする間ではありません

// 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

            }
        });

async taskの前にリストをクリアしていたため、Java.lang.IndexOutOfBoundsException: Inconsistency detected.になるので、pull-refreshの間にスクロールすると、これが起こることを発見しました。

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

そうすれば、矛盾が生じることはありません。

2
Fahad

私は同じ状況に直面しました。コレクションをクリアする前にコードを追加して解決しました。

mRecyclerView.getRecycledViewPool().clear();

1
MagicDroidX

この問題は、リストをクリアしようとしたときに発生する可能性があります。特に、pullを使用してリフレッシュするときにデータリストをクリアする場合は、ブール値フラグを使用してください。 flagが新しいデータを追加する直前にtrueであれば、それ以降は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() ; 
 }

}
1
Moaz H

私は最近、新しいAndroid Architecture Componentsでこの厄介なスタックトレースを見つけました。基本的に、LiveDataを使用して、自分のFragmentで観察されるViewModel内の項目のリストがあります。 ViewModelがデータに新しい値を送信すると、Fragmentはアダプタを更新し、これらの新しいデータ要素を渡して、アダプタに変更があったことを通知します。

残念ながら、新しいデータ要素をアダプタに渡すときに、ViewModelとアダプタの両方が同じオブジェクト参照を指しているという事実を説明できませんでした。つまり、ViewModel内でデータを更新してpostValue()を呼び出すと、データが更新される可能性がある非常に小さなウィンドウがあり、アダプタにはまだ通知されていません。

私の解決策は、アダプタに渡されたときに要素の新しいコピーをインスタンス化することでした:

mList = new ArrayList<>(passedList);

この非常に簡単な修正により、アダプタに通知される直前までアダプタデータが変更されないようにすることができます。

1
Steve

私は似たような問題に遭遇し、そしてちょうどそれを考え出しました。テストケース用にいくつかの例をハードコーディングしましたが、それらがそれぞれ一意のIDを返すことを保証するものではなかったため、以下のクラッシュが発生しました。 IDを修正することで問題が解決しました。これが他の人に役立つことを願っています!

1
DocBot

私の場合はsetHasStableIds(true);で行を削除しました

1
darkchaos

また、アダプタを同時に複数回設定することにも関連する可能性があります。同時に5〜6回起動されるコールバックメソッドがあり、そのコールバックでアダプタを設定していたため、RecycledViewPoolがこれらすべてのデータを同時に処理できませんでした。それは大きなチャンスですが、とにかくそれをチェックした方がいいでしょう。

1
Mustafa Güven

通知する前に、レイアウトマネージャのすべてのビューを削除するだけです。好きです:

myLayoutmanager.removeAllViews();
1
BaBaX Ra

これはかなり厄介なバグです。

私のアイテムのクリックを処理するために、私は この質問 にある解決策と同様のRecyclerView.OnItemTouchListenerの実装を使いました。

RecyclerViewのデータソースを何度も更新して項目をクリックした後、このIndexOutOfBoundsExceptionは私のアプリケーションをクラッシュさせるでしょう。アイテムがクリックされると、RecyclerViewは内部的に正しい基礎となるビューを探し、その位置を返します。ソースコードを調べてみると、TasksThreadsがスケジュールされていることがわかりました。話を簡単にすると、基本的には2つのデータソースが混在し、同期されていないという、まったく違法な状態です。

これに基づいて、私は私のRecyclerView.OnItemTouchListenerの実装を削除し、ViewHolderAdapterをクリックしました。

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

}

これは最善の解決策ではないかもしれませんが、今のところクラッシュフリーです。うまくいけば、これで時間が節約できます:)。

0
DroidBender

add_location.removeAllViews();

            for (int i=0;i<arrayList.size();i++)
            {
                add_location.addView(new HolderDropoff(AddDropOffActivtity.this,add_location,arrayList,AddDropOffActivtity.this,this));
            }
            add_location.getAdapter().notifyDataSetChanged();
0
Manish Sharma

Lintが矛盾についてアドバイスしてくれました。(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を実行してください。

0
Rodolphe Calvet

私も一度エラーを得ました:

原因:古い削除されたviewHoldersを同時に取得しようとしているときに、AsyncタスクからRecycler Viewを更新しようとしました。

コード:ボタンを押すだけでデータが生成されます。ロジックは以下の通りです。

  1. リサイクル業者ビューの最後の品目をクリアする
  2. 非同期タスクを呼び出してデータを生成する
  3. OnPostExecute RecyclerビューとNotifyDataSetChangedを更新する

問題:データを生成する前に速くスクロールするときはいつでも

矛盾が検出されました。無効なビューホルダアダプタpositionViewHolder Java.lang.IndexOutOfBoundsException:矛盾が検出されました。無効な商品位置20(オフセット:2)、状態:3

解決策:私のデータを生成する前にRecyclerViewをクリアする代わりに、私は代わりにそれを残して、次に示すように新しいデータと交換します;

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

私にとっては、このコード行を追加した後でうまくいきました。

mRecyclerView.setItemAnimator(null);
0
Fatih Gee

私が認識していなかったときに問題が発生しました。異なるスレッドで同時に2回呼び出しました。

notifyDataSetChanged

sqliteロード関数から1つ、関数呼び出し後から1つ

0
Beyaz

私はこの問題を解決し、新しいデータを取得するときに項目を一つずつ追加しました。アダプター内でこの機能を使用しています。

public void add(Data item) {
        if(!params.contains(item){
           params.add(item);
           notifyItemInserted(getItemCount() - 1);
        }
    }
}
0
César Cobo

申し訳ありませんが完璧な解決策:特定のアイテムを削除しようとするときにnotifydatasetchange()を呼び出してそのアイテムをbindviewholderで取得し、そのアイテムを削除してから最後のリストに追加してください。項目。基本的にあなたが中心からアイテムを取り除こうとするとき、問題は来ます。最後のインデックスから項目を削除すると、それ以上リサイクルは行われず、追加者の数も重要ではなくなり(これはここで重要なクラッシュになります)、クラッシュは以下のコードスニペットで解決されます。

 holder.itemLayout.setVisibility( View.GONE );//to hide temprory it show like you have removed item

        Model current = list.get( position );
        list.remove( current );
        list.add( list.size(), current );//add agine to last index
        if(position==list.size()-1){// remove from last index
             list.remove( position );
        }
0
Irfan Ullah

私は以前に同じ問題を抱えていました。最後にそれに対する回避策を見つけました

アイテムが削除されたことをアダプタに通知し、アダプタデータセットの範囲が変更されたことを通知する

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

 }
0
Cheng

私の場合は、バックグラウンドスレッドでアダプタの内容を変更しようとしましたが、メイン/ uiスレッドではnotify *を呼び出しました。

これは不可能です!notifyがメインスレッドに強制されるのは、同じコールスタック上であっても、recyclerviewがメインスレッド上でバッキングアダプタを編集することを望んでいるためです。

問題を解決するには、アダプタへのすべての操作とnotify ...呼び出しのすべてがui /メインスレッドで行われるようにします。

これは私のために働いた唯一の解決策であり、上記の解決策から多くを試してもいます。

1.)殺菌

CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);

2.)アダプタにこのメソッドを書く

public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}

stockListModels - >このリストはアダプタで使用しているものです。

0
Ramkesh Yadav

非常に遅い応答ですが、これは機能に関する誰かを助けるかもしれません。

OnStopまたはonPauseメソッドがリストをクリアしていないことを確認してください

0
just aguy

私はその設定mRecycler.setLayoutFrozen(true)を見つけました。 swipeContainerのonRefreshメソッドで。

私のために問題を解決しました。

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

        }
    });
0
Mark Sheekey