Recyclerview onCreateViewHolder вызывается несмотря на то, что все представления перерабатываются? - PullRequest
0 голосов
/ 21 февраля 2019

Короткая история: У меня есть (предварительное кэширование) Custom LinearLayoutManager, RecyclerView, Custom RecyclerView.Adaptor, Custom RecyclerView.ViewHolder setup.У меня есть только один viewType и облегченные функции привязки.Это действительно ничего особенного, поэтому я надеюсь, что мне не нужно размещать код.Я также не хочу путать со всем нерелевантным кодом.

Проблема, с которой я столкнулся, заключается в том, что, несмотря на то, что не удается перезапустить какие-либо представления, onCreateViewHolder по-прежнему вызывается время от времени (после начальных раздуваний строк), что заставляет меня задуматься о том, чтоможет у меня утечка памяти?Как вы думаете, это так и почему?Что решает, что моему приложению все еще нужно создавать больше представлений, а не перерабатывать?

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

Полная история: Я заметил случайный сбой в плавной прокрутке моего RecyclerView.Используя профилировщик android studio, я заметил следующее:

  1. Все мои bindViewHolder методы достаточно быстрые и не сдерживают прокрутку.
  2. OnCreateViewHolder - вот что вызываетзаиканиеЭто объясняет, почему во время первой прокрутки всегда есть несколько заиканий.Более того, это инфляция, которая занимает смехотворно высокий процент времени процессора.
  3. С разметкой элемента / строки, построенной с использованием ConstraintLayout, низкая производительность функций onMeasure снизила производительность прокрутки на более слабых устройствах..
  4. При компоновке элемента / строки, построенной с использованием LinearLayout s, производительность значительно улучшилась.Однако раздувание представлений по-прежнему занимает достаточно много времени, чтобы вызвать всплески.

С этой информацией я максимально упростил макет элемента строки, убедившись, что он использует LinearLayout s.Несмотря на это, рендеринг элементов в окне повторного просмотра не должен приводить к заиканию после появления первых элементов на экране, поскольку, во-первых, строки одинаковы, за исключением данных, связанных с ними, и двух, RecyclerView должен перерабатывать строки.Таким образом, onCreateViewHolder следует сначала назвать много, а затем редко.Как насчет предварительного кэширования?Я обнаружил, что это одна из причин, по которой при прокрутке запрашиваются новые пользователи.Я установил кэш и создал пользовательский LinearLayoutManager, который переопределяет метод предварительного кэширования (pre-fetching?), Называемый getExtraLayoutSpace(RecyclerView.State state), и настроил их так, чтобы было достаточно существующих перерабатываемых представлений для покрытия запроса во время прокрутки.Мои тесты подтверждают, что после начальной прокрутки новые виды не запрашиваются при переходе в состояние прокрутки.

Все это и у меня осталось две проблемы.Одним из них является то, что onCreateViewHolder вызывается очень часто во время использования приложения, и это вызывает небольшой сбой.Я поместил Log.w() внутри onFailedToRecycleView(), чтобы увидеть, что любые виды не перерабатываются, и похоже, что виды перерабатываются.Так что теперь я думаю, что есть некоторая утечка памяти, и профилировщик памяти показывает скачки в использовании памяти, часто происходящие, когда вызывается onCreateViewHolder.

1 Ответ

0 голосов
/ 03 марта 2019

Три вопроса, которые я опубликовал, где,

  1. Есть ли утечка памяти, потому что Recyclerview продолжает создавать виджеты?
  2. Почему у меня утечка памяти?
  3. Что решает, что моему приложению все еще нужно создавать больше представлений, а не перерабатывать?

Я полагаю, что по сути нашел решение 2 из 3 вопросов или соответствующих проблем.

Внутри класса Recyclerview есть класс Recycler и класс RecycledViewPool. Внутренний объект Recycler - это то, что делает запрос к объекту RecycledViewPool для доступного отдельного держателя просмотра отходов для переработки , если у него еще нет прикрепленного держателя просмотра отходов для переработки.И, в частности, этот поиск перерабатываемого держателя просмотра происходит внутри функции Recycler getViewForPosition(int position).Если (внутри этой функции) перерабатываемое представление не найдено, то вызывается mAdapter.createViewHolder(RecyclerView.this, type).Это приводит к вызову onCreateViewholder.Таким образом, чтобы ответить на 3-й вопрос, Recycler решает, когда вызывать onCreateViewholder.

Обратите внимание, что задача RecycledViewPool состоит в том, чтобы поддерживать массив списков держателей отдельных записок (массив 1d массива каждого представления viewType)).Поддерживающие функции включают clear(), reset() и setMaxScrap(int viewType, int max), которые выполняют то, что указывают их имена.Таким образом, путь к решению второго вопроса заключается в создании пользовательского RecycledViewPool, который указывает, когда скраплист очищается или уменьшается, чтобы я мог отслеживать создание и уничтожение представлений скрапа.Что касается вопроса 1, это можно решить с помощью setMaxScrap(int viewType, int max).Кроме того, при инициализации окна повторного просмотра мы можем поместить несколько дополнительных просмотров в пул и попытаться сохранить минимальное количество просмотров, которое, вероятно, можно выполнить с помощью прослушивателя.

...