Recyclerview onCreateViewHolder вызывается несмотря на то, что все представления перерабатываются?
Короткая история: у меня есть (предварительное кэширование) Custom LinearLayoutManager
, RecyclerView
, Custom RecyclerView.Adaptor
, Custom RecyclerView.ViewHolder
настроить. У меня есть только один viewType и облегченные функции привязки. Это действительно ничего особенного, поэтому я надеюсь, что мне не нужно размещать код. Я также не хочу путать со всем несоответствующим кодом.
Проблема, с которой я столкнулся, заключается в том, что, несмотря на то, что не удается перезапустить какие-либо представления, onCreateViewHolder по-прежнему вызывается время от времени (после начального надувания строк), что заставляет меня задуматься о возможной утечке памяти? Как вы думаете, это так и почему? Что решает, что моему приложению по-прежнему нужно создавать больше представлений, а не перерабатывать?
Я добавлю одну вещь, которая может быть как-то с учетом. Мои строки имеют два визуальных состояния (они расширяются и сворачиваются), и кажется, что наличие более случайного сочетания строк в разных состояниях усугубляет проблему.
Полная история: я заметил случайный сбой в плавной прокрутке моего RecyclerView
, Используя профилировщик android studio, я заметил следующее:
- Все мое
bindViewHolder
методы очень быстрые и не сдерживают прокрутку. OnCreateViewHolder
это то, что вызывает заикание. Это объясняет, почему во время первой прокрутки всегда есть несколько заиканий. Кроме того, это инфляция, которая занимает смехотворно высокий процент времени процессора.- С макетом элемента / строки, построенным с использованием
ConstraintLayout
,onMeasure
плохая производительность функций, разрушил производительность прокрутки на более слабых устройствах. - С макетом элемента / строки, построенным с использованием
LinearLayout
с, производительность резко улучшилась. Тем не менее, инфляция мнений все еще занимает достаточно много времени, чтобы вызвать взлеты.
Имея эту информацию, я максимально упростил макет элемента строки, убедившись, что он используется LinearLayout
s. Несмотря на это, рендеринг элементов в окне повторного просмотра не должен приводить к заиканию после появления первых элементов на экране, поскольку, во-первых, все строки одинаковы, за исключением данных, связанных с ними, и двух, RecyclerView
должен перерабатывать строки. Так onCreateViewHolder
сначала нужно вызвать много, а потом снова редко. Как насчет предварительного кэширования? Я обнаружил, что это одна из причин, по которой при прокрутке запрашиваются новые пользователи. Я установил кеш и создал кастом LinearLayoutManager
который переопределяет метод предварительного кэширования (pre-fetching?), называемый getExtraLayoutSpace(RecyclerView.State state)
и скорректировал их так, чтобы было достаточно существующих перерабатываемых видов для покрытия запроса во время прокрутки. Мои тесты подтверждают, что после начальной прокрутки новые виды не запрашиваются при переходе в состояние прокрутки.
Все это и у меня осталось две проблемы. Одним из них является то, что onCreateViewHolder
вызывается очень часто во время использования приложения, и это вызывает небольшой сбой. Я положил Log.w()
внутри onFailedToRecycleView()
чтобы увидеть, что любые виды не перерабатываются, и похоже, что виды перерабатываются. Так что теперь я думаю, что есть некоторая утечка памяти, и профилировщик памяти показывает скачки в использовании памяти, часто происходящие, когда onCreateViewHolder
называется.
1 ответ
Два вопроса, которые я разместил где,
- У меня утечка памяти, потому что Recyclerview продолжает создавать виджеты?
- Почему у меня утечка памяти?
- Что решает, что моему приложению по-прежнему нужно создавать больше представлений, а не перерабатывать?
Я считаю, что по сути я нашел решение 2 из 3 вопросов или соответствующих основных проблем.
внутри Recyclerview
класс Recycler
класс и RecycledViewPool
учебный класс. внутренний Recycler
объект это то, что делает запрос к RecycledViewPool
объект для доступного отдельного держателя скрапа для переработки, если у него еще нет прикрепленного держателя скрапа для переработки. И, в частности, этот поиск перерабатываемого держателя просмотра происходит внутри Recycler
"s 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)
, Кроме того, при инициализации просмотра повторного просмотра мы можем поместить несколько дополнительных просмотров в пул и попытаться сохранить минимальное количество просмотров, которое, вероятно, можно выполнить с помощью прослушивателя.