NotifyDataSetChanged- RecyclerView - это асинхронный вызов?

Я пытаюсь следовать набору заявлений после выполнения notifyDataSetChanged на переработчик вид. Но когда я отлаживаю свое приложение, отладчик достигает следующих нескольких строк после моего notifyDataSetChanged прежде чем идти к onBindViewHolder адаптера утилита просмотра. Итак, мой вопрос: является ли notifyDataSetChanged асинхронным вызовом? Если да, получим ли мы обратный звонок? PS: я уже гуглил этот ответ и не смог найти подходящий ответ, поэтому и спрашиваю сообщество.

2 ответа

Решение

Recycler Посмотрите автора здесь,

Когда вы звоните notifyDataSetChanged, RecyclerView делает данные недействительными, но пользовательский интерфейс не обновляется до следующего кадра анимации. Вот как работает система Android View. Когда виджет становится недействительным (например, изменяя свои данные), он запрашивает компоновку, что означает, что он будет переизмерен и перекомпонован при следующем обходе представления. Это сделано для того, чтобы мы могли пакетировать все изменения до следующего обновления экрана. Вот почему notifyDataSetChange не вызывает onBind мгновенно.

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

Причина этого ограничения состоит в том, что если набор данных изменяется во время макета, менеджеру макета очень трудно восстановить стабильное состояние (например, представьте вызовы RecyclerView onBind(5) и пункт 5 одновременно удаляется в другой ветке). Кроме того, учет таких изменений потребует большой синхронизации, что приведет к значительному снижению производительности без какой-либо выгоды. Вот почему все компоненты пользовательского интерфейса являются однопоточными.

TLDR: Нет. Вы можете позвонить notifyDatasetChanged() только из потока пользовательского интерфейса, как указано здесь, который вносит изменения в пользовательский интерфейс, которые могут быть сделаны только в основном потоке. Это делает вызов синхронным.

Этот вызов предназначен для перерисовки интерфейса с измененными элементами. Это похоже на вызов requestFocus() на вид.

Из документации:

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

Поскольку представления перерисовываются, так как набор данных ожидает изменения, он должен находиться в основном потоке пользовательского интерфейса (только основной петлитель (т. Е. Поток пользовательского интерфейса) может изменять представления).

Вы можете иметь несколько AdapterObservers, которые могут выполнять необходимые вам действия в пользовательском интерфейсе.

Также из документации:

  • Это событие не указывает, что в наборе данных изменилось, заставляя * любых наблюдателей предполагать, что все существующие элементы и структура могут быть недействительными. * LayoutManager будет вынужден полностью перепривязать и ретранслировать все видимые виды.

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

 public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }

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

Я столкнулся с этой проблемой, потому что мне нужно было выполнить некоторую реконфигурацию макета в зависимости от размера моего recyclerview, а поскольку у меня была высота, установленная на wrap_content, размер менялся всякий раз, когда менялись его данные. Чтобы эффективно реагировать на изменения макета, вы можете использовать короткий подкласс вашего менеджера макета (в моем случае это был LinearLayoutManager) и переопределить onLayoutCompleted:

recyclerView.setLayoutManager(new LinearLayoutManager(getContext()){
        @Override
        public void onLayoutCompleted (RecyclerView.State state){
            super.onLayoutCompleted(state);
            // update UI for new layout
        }
    });

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

Другие вопросы по тегам