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

Ответ 1

У меня была (возможно) связанная проблема - ввод нового экземпляра действия с помощью RecyclerView, но с меньшим адаптером вызывал этот сбой для меня.

RecyclerView.dispatchLayout() может попытаться mRecycler.clearOldPositions() элементы из mRecycler.clearOldPositions() перед вызовом mRecycler.clearOldPositions(). Следствием этого является то, что он вытягивал элементы из общего пула, позиции которых превышали размер адаптера.

К счастью, он делает это только в том случае, если включены PredictiveAnimations, поэтому мое решение состояло в том, чтобы LinearLayoutManager подкласс 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);
    }
}

Ответ 2

В моем случае (удалить/вставить данные в мою структуру данных) мне нужно было очистить пул утилизации, а затем изменить набор данных!

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

Ответ 3

Используйте notifyDataSetChanged() вместо notifyItem... в этом случае.

Ответ 4

Я решил это, отложив mRecycler.setAdapter(itemsAdapter) до добавления всех элементов в адаптер с помощью mRecycler.addAll(items), и он сработал. Не знаю, почему я сделал это для начала, это был из кода библиотеки, который я просмотрел, и увидел эти строки в "неправильном порядке", я уверен, что это все равно, пожалуйста, если кто-то может подтвердить это, объясните, почему это так? Не уверен, что это правильный ответ даже

Ответ 5

У меня была аналогичная проблема, но не совсем то же самое. В моем случае в 1 точке я очищал массив, который был передан в recyclerview

mObjects.clear();

и не вызывать notifyDataSetChanged, так как я не хотел, чтобы recyclerview немедленно очистил представления. Я повторно заполнил массив mObjects в AsyncTask.

Ответ 6

У меня есть одна и та же проблема. Это происходило, когда я быстро прокручивался и вызывал API и обновлял данные. После попытки всех вещей предотвратить крах я нашел решение.

mRecyclerView.stopScroll();

Он будет работать.

Ответ 7

Моя проблема исчезла после того, как я модифицировал реализацию 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);
}

Ответ 8

У меня была такая же проблема с recyclerView, поэтому я просто уведомил адаптер об изменении набора данных сразу после очистки списка.

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

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

Ответ 9

Эта ошибка возникает, когда список в адаптере очищается при прокрутке пользователя, при которой изменяется положение держателя элемента, теряется ссылка между списком и элементом на пользовательском интерфейсе, возникает ошибка в следующем запросе "notifyDataSetChanged".

Fix:

Проверьте ваш метод обновления списка. Если вы делаете что-то вроде

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

===> Error occur

Как исправить. Создайте новый объект списка для обработки буфера и после этого снова назначьте основному списку

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

Спасибо Нхан Цао за эту большую помощь :)

Ответ 10

Используйте

notifyDataSetChanged()

вместо

notifyItemRangeInserted(0, YourArrayList.size())

в этом случае.

Ответ 11

Я изменяю данные для RecyclerView в фоновом режиме Thread. Я получил тот же Exception, что и OP. Я добавил это после изменения данных:

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

Надеюсь, что это поможет

Ответ 12

Чтобы устранить эту проблему, просто вызовите notifyDataSetChanged() с пустым списком перед обновлением вида перезаписи.

Например

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear();//Список для просмотра в режиме обновления

adapter.notifyDataSetChanged();

Ответ 13

В моем случае я обновлял элементы и вызывал notifyDataSetChanged в потоке, отличном от UI. Большую часть времени он работал, но когда много изменений произошло быстро, это сработало. Когда я это сделал, вместо этого, в основном

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

то он перестает сбой.

Ответ 14

Вам нужно только очистить список на 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

            }
        });

Я обнаружил, что это происходит при прокрутке во время pull для обновления, так как я очищал список до async task, в результате получилось java.lang.IndexOutOfBoundsException: Inconsistency detected.

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

Таким образом, вы не закончите несогласованность

Ответ 15

Недавно я столкнулся с этой неприятной трассировкой стека с новыми компонентами архитектуры Android. По сути, у меня есть список элементов в моей ViewModel, которые наблюдаются моим фрагментом, используя LiveData. Когда ViewModel публикует новое значение для данных, фрагмент обновляет адаптер, передавая эти новые элементы данных и уведомляя адаптер об изменениях.

К сожалению, при передаче новых элементов данных в адаптер я не смог учесть тот факт, что и ViewModel, и Adapter будут указывать на одну и ту же ссылку на объект! Это означает, что если я postValue() данные и вызову postValue() из ViewModel, появится очень маленькое окно, в котором данные могут быть обновлены, а адаптер еще не уведомлен!

Мое исправление состояло в том, чтобы создать новую копию элементов при передаче в адаптер:

mList = new ArrayList<>(passedList);

С этим очень простым исправлением вы можете быть уверены, что данные вашего адаптера не изменятся до тех пор, пока ваш адаптер не будет уведомлен.

Ответ 16

эта проблема может возникнуть при попытке очистить список, если вы собираетесь очистить свой список данных, особенно когда вы используете pull для обновления, попробуйте использовать логический флаг, инициализировать его как false и внутри метода OnRefresh сделать его истинным, очистите свой список данных, если флаг равен true перед добавлением к нему новых данных, и после этого сделайте его ложным.

ваш код может выглядеть следующим образом

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

}

Ответ 17

Он также может быть связан с настройкой адаптера несколько раз в одно и то же время. У меня был метод обратного вызова, который запускался 5-6 раз в то же время, и я настраивал адаптер в этом обратном вызове, так что RecycledViewPool не мог обрабатывать все эти данные одновременно. Это жирный шанс, но вам все равно лучше проверить его.

Ответ 18

Я столкнулся с такой же ситуацией. И это было решено путем добавления кодов, прежде чем вы очистите свою коллекцию.

mRecyclerView.getRecycledViewPool().clear();

Ответ 19

Я столкнулся с подобной проблемой и просто понял это. Я жестко закодировал несколько примеров для тестового примера, но не гарантировал, что каждый из них вернул уникальный идентификатор, и это вызвало ошибку ниже. Исправление идентификаторов разрешило проблему, надеюсь, что это поможет кому-то еще!

Ответ 20

В моем случае я просто удалил строку с setHasStableIds(true);

Ответ 21

В моем случае я пытался изменить содержимое моего адаптера в фоновом потоке, но вызвал notify * в основном/пользовательском потоке.

Это невозможно! Причина, по которой уведомления вынуждены выполнять основной поток, заключается в том, что администратор повторного просмотра требует, чтобы вы отредактировали резервный адаптер в основном потоке, даже в том же стеке вызовов.

Чтобы решить эту проблему, убедитесь, что каждая операция с вашим адаптером, а также каждый вызов notify... выполняется в потоке пользовательского интерфейса /main !

Ответ 22

Просто удалите все представления диспетчера макетов перед уведомлением. как:

myLayoutmanager.removeAllViews();

Ответ 23

Для меня это сработало после добавления этой строки кода:

mRecyclerView.setItemAnimator(null);

Ответ 24

Я решил проблему, добавляя элементы один за другим, когда он получает новые данные. Я использую эту функцию внутри адаптера.

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

Ответ 25

Я обнаружил, что установка mRecycler.setLayoutFrozen(истина); в методе onRefresh для swipeContainer.

решил проблему для меня.

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

        }
    });

Ответ 26

У меня была такая же проблема ранее. Наконец нашел обходной путь для этого

Что я делаю, чтобы уведомить адаптер, который удалил элемент, а затем уведомил диапазон набора данных адаптера.

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

 }

Ответ 27

Это довольно неприятная ошибка.

Чтобы обработать мой элемент, я использовал реализацию RecyclerView.OnItemTouchListener, аналогичную решению, найденному в этом вопросе.

После многократного обновления источника данных RecyclerView и нажатия элемента этот IndexOutOfBoundsException приведет к сбою моего приложения. Когда элемент щелкнут, RecyclerView внутренне ищет нужный основной вид и возвращает его позицию. Выбрав исходный код, я увидел, что запланировано несколько Tasks и Threads. Короче говоря, в основном это просто незаконное государство, в котором два источника данных смешиваются и не синхронизируются, и все это идет вразрез.

Исходя из этого, я удалил реализацию RecyclerView.OnItemTouchListener и просто нажал на ViewHolder Adapter сам:

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

}

Это может быть не самое лучшее решение, но без сбоев. Надеюсь, это сэкономит вам время:).

Ответ 28

i тоже получил ошибку:

Причина: Я пытался обновить View Recycler из задачи Async, одновременно пытаясь получить старые удаленные файлы просмотра,

Код: Я генерирую данные нажатием кнопки, логически следуя

  • Удалить последние элементы в представлении ресайклера
  • Вызов задачи async для генерации данных
  • OnPostExecute Обновить представление Recycler и NotifyDataSetChanged

Проблема: Всякий раз, когда я быстро прокручиваю перед генерированием своих данных, я получаю

Обнаружена несогласованность. Недействительный адаптер держателя держателя ViewViewer java.lang.IndexOutOfBoundsException: обнаружена несогласованность. Недопустимое положение позиции 20 (смещение: 2).state: 3

Решение: вместо очистки RecyclerView перед созданием моих данных, я вместо этого оставлю его, а затем заменим его новыми данными - Call NotifyDatasetChanged, как показано ниже;

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

Ответ 29

Линт дал мне совет относительно несогласованности: Я написал (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 для полного объяснения!

Ответ 30

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