Действия Android в RecyclerView

Недавно я немного поиграл с RecyclerView и концепцией ViewHolder в Android 5+. Реализация адаптера обрабатывает довольно большой набор данных (т. е. около 60 000 элементов), запрашиваемых из Интернета при вызове onBindViewHolder(...). Сетевые запросы обрабатываются библиотекой volley, а ответ представляет собой модель данных, используемую для заполнения держателя представления.

Теперь при быстром перемещении (т. е. движении со скоростью, отличной от смахивания, когда пользователь касается устройства и медленно перемещается по экрану) onBindViewHolder вызывается для всех позиций в адаптере, что не является Хорошо, что большинство вьюхолдеров не видно из-за высокой скорости анимации. Например, если пользователь проводит пальцем от позиции 5 до позиции 300 за считанные секунды, большинство представлений невозможно прочитать, но вызывается метод привязки. Так как адаптер получает модель данных из сети - делается много запросов, большинство из них бесполезны, а процесс дыры просто задерживает обработку запросов вокруг позиции 300, которую реально может наблюдать пользователь.

Как можно подойти к этой проблеме запроса только видимых позиций? Мой первый подход состоял в том, чтобы приоритизировать запросы в очереди обработки залпа. На самом деле это не решило большую часть проблемы, поскольку каждый сетевой запрос обрабатывается просто в другом порядке. Мой второй подход заключался в том, чтобы отменить все существующие запросы при добавлении нового. Это не сработало для меня очень хорошо, и с точки зрения дизайна мне это решение не нравится.

Тогда мой реальный вопрос заключается в том, как бы вы подошли к этому вопросу?


person Faur Ioan-Aurel    schedule 13.04.2015    source источник
comment
Вы пришли к какому-либо выводу по этому поводу? У меня та же проблема с использованием NetworkImageView Volley.   -  person LeoFarage    schedule 23.07.2015


Ответы (1)


Да, мне удалось исправить свои проблемы, сохранив состояние каждой позиции адаптера. RecyclerView.Adapter имеет два метода onViewAttachedToWindow(VH holder) и onViewDetachedFromWindow(VH holder), которые сигнализируются, когда представление собирается быть увиденным пользователем или когда оно удаляется, чтобы пользователь не мог его видеть.

Я создал простой одноэлементный класс, чтобы сохранить состояние каждой позиции адаптера:

import java.util.HashSet;
import java.util.Set;

/**
 * Created by fauri on 14/06/2015.
 */
public class ViewHolderState {
    private static final ViewHolderState ourInstance = new ViewHolderState();
    private Set<Integer> recycled = new HashSet<>();

    private ViewHolderState() {
    }

    public static ViewHolderState getInstance() {
        return ourInstance;
    }

    public void addRecycledView(int position) {
        recycled.add(position);
    }

    public void removeRecycledView(int position) {
        recycled.remove(position);
    }

    public boolean isViewRecycled(int position) {
        return recycled.contains(position);
    }

    public void clearAll() {
        recycled.clear();
    }
}

Находясь в классе адаптера:

 private ViewHolderState viewHolderState = ViewHolderState.getInstance();


 @Override
    public void onViewDetachedFromWindow(CardViewHolder holder) {
        super.onViewDetachedFromWindow(holder);
        viewHolderState.addRecycledView(holder.getAdapterPosition());
    }

    @Override
    public void onViewAttachedToWindow(CardViewHolder holder) {
        super.onViewAttachedToWindow(holder);
        viewHolderState.removeRecycledView(holder.getAdapterPosition());
    }

Это просто для сохранения состояния каждой позиции адаптера. Теперь в бизнес-логике, которая инициирует сетевые вызовы, вам просто нужно получить экземпляр синглтона и проверить, есть ли isViewRecycled(int adapterPosition). Если представление переработано, нет необходимости делать сетевой вызов.

Конечно, это простое решение. Код можно улучшить, добавив потокобезопасность (то есть либо используйте Collection.synchronizedSet(...), либо сделайте методы синхронизированными), либо просто используйте другой шаблон. Гуру Java на самом деле не копают одноэлементный шаблон.

В любом случае, общая идея состоит в том, чтобы использовать onViewAttachedToWindow и onViewDetachFromWindow.

person Faur Ioan-Aurel    schedule 27.07.2015