android - viewtype - recyclerview two view types




Утечка канарейка, Recyclerview утечка mAdapter (3)

Я решил, что пришло время узнать, как использовать Leak Canary для обнаружения утечек в моих приложениях, и, как всегда, я попытался внедрить его в свой проект, чтобы действительно понять, как использовать инструмент. Реализовать это было достаточно легко, сложная часть заключалась в том, чтобы прочитать, что инструмент мне бросает. У меня есть представление прокрутки, которое, по-видимому, накапливает память в диспетчере памяти при прокрутке вверх и вниз (даже если она не загружает новые данные), поэтому я подумал, что это хороший объект-кандидат для отслеживания утечек, вот результат:

Похоже, v7.widget.RecyclerView протекает адаптер, а не мое приложение. Но это не может быть правдой .... верно?

Вот код для адаптера и класс, использующий его: https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e

Я получил награду за этот вопрос, потому что он появился через два года в совершенно другом приложении


Мне удалось это исправить, переопределив RecyclerView. Это происходит потому, что RecyclerView никогда не отменяет свою регистрацию в AdapterDataObservable.

@Override protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (getAdapter() != null) {
        setAdapter(null);
    }
}

Прежде всего, я ссылаюсь на этот файл .

Похоже, v7.widget.RecyclerView протекает адаптер, а не мое приложение. Но это не может быть правдой .... верно?

На самом деле ваш адаптер пропускает RecyclerView (и это довольно ясно видно по графику трассировки и названию действия LeakCanary). Однако я не уверен, является ли это «родительским» RecyclerView или вложенным в HourlyViewHolder, или и тем, и другим. Я думаю, что виноваты ваши ViewHolders. Делая их нестатическими внутренними классами, вы явно предоставляете им ссылку на класс вложенного адаптера, и это почти напрямую itemView адаптер с переработанными представлениями, поскольку родителем каждого itemView в ваших держателях является сам RecyclerView.

Моим первым предложением, чтобы решить эту проблему, было бы разделить ваши ViewHolders и Adapter, сделав их статическими внутренними классами. Таким образом, они не содержат ссылку на адаптер, поэтому ваше поле контекста будет для них недоступным, и это также хорошо, поскольку ссылки на контекст должны передаваться и сохраняться экономно (также во избежание больших утечек памяти). Когда вам нужен контекст только для получения строк, сделайте это где-нибудь еще, например, в конструкторе адаптера, но не сохраняйте контекст как член. Наконец, DayForecastAdapter кажется опасным: вы передаете один и тот же его экземпляр каждому HourlyViewHolder , что выглядит как ошибка.

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


Если адаптер живет дольше, чем RecyclerView , вам нужно очистить ссылку на адаптер в onDestroyView :

@Override
public void onDestroyView() {
    recyclerView.setAdapter(null);
    super.onDestroyView();
}

В противном случае адаптер будет содержать ссылку на RecyclerView который уже должен был не хватить памяти.





leakcanary