Утечка канарейки, рециркуляция утечки mAdapter
Я решил, что настало время узнать, как использовать Leak Canary для обнаружения утечек в моих приложениях, и, как я всегда это делаю, я попытался реализовать его в своем проекте, чтобы действительно понять, как использовать этот инструмент. Реализация этого была достаточно простой, сложная часть читала то, что инструмент отбрасывает мне.
У меня есть scrollview, который, похоже, накапливает память в диспетчере памяти, когда я просматриваю вверх и вниз (даже если он не загружает никаких новых данных), поэтому я подумал, что это хороший объект-кандидат для отслеживания утечек, вот результат:
![введите описание изображения здесь]()
Похоже, что v7.widget.RecyclerView просачивает адаптер, а не мое приложение. Но это не может быть прав... правильно?
Здесь приведен код адаптера и класс, использующие его:
https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e
Я начал щедрость за этот вопрос, потому что он снова появился после двух лет на совершенно другом приложении
Ответы
Ответ 1
Если адаптер живет дольше, чем RecyclerView
, вам нужно очистить ссылку на адаптер в onDestroyView
:
@Override
public void onDestroyView() {
recyclerView.setAdapter(null);
super.onDestroyView();
}
В противном случае адаптер будет содержать ссылку на RecyclerView
который уже должен был не хватить памяти.
Если экран задействован в анимации перехода, вам действительно нужно сделать еще один шаг вперед и очистить адаптер только после отсоединения вида:
@Override
public void onDestroyView() {
recyclerView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
// no-op
}
@Override
public void onViewDetachedFromWindow(View v) {
recyclerView.setAdapter(null);
}
});
super.onDestroyView();
}
Ответ 2
Я смог исправить это, переопределив RecyclerView. Это происходит потому, что RecyclerView никогда не отменяет себя от AdapterDataObservable.
@Override protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (getAdapter() != null) {
setAdapter(null);
}
}
Ответ 3
Прежде всего, я ссылаюсь на этот файл.
Похоже, v7.widget.RecyclerView протекает адаптер, а не мое приложение. Но это не может быть правдой.... верно?
Это на самом деле ваш адаптер, который пропускает RecyclerView
(и это довольно ясно видно по графику трассировки и названию действия LeakCanary). Однако я не уверен, является ли он "родительским" RecyclerView или вложенным в HourlyViewHolder, или и тем, и другим. Я думаю, что виноваты ваши ViewHolders. Делая их нестатическими внутренними классами, вы явно предоставляете им ссылку на класс вложенного адаптера, и это почти напрямую itemView
адаптер с переработанными представлениями, поскольку родителем каждого itemView
в ваших держателях является сам RecyclerView.
Моим первым предложением, чтобы решить эту проблему, было бы разделить ваши ViewHolders и Adapter, сделав их статическими внутренними классами. Таким образом, они не содержат ссылку на адаптер, поэтому ваше поле контекста будет для них недоступным, и это также хорошо, поскольку ссылки на контекст должны передаваться и сохраняться экономно (также во избежание больших утечек памяти). Когда вам нужен контекст только для получения строк, сделайте это где-нибудь еще, например, в конструкторе адаптера, но не сохраняйте контекст как член. Наконец, DayForecastAdapter
кажется опасным: вы передаете один и тот же его экземпляр каждому HourlyViewHolder
, что выглядит как ошибка.
Я думаю, что исправление дизайна и разделение этих классов должны избавить от этой утечки памяти
Ответ 4
Я не могу открыть ваше изображение и увидеть фактическую утечку, но если вы определяете локальную переменную для RecyclerView
в Fragment
и устанавливаете свой фрагмент retainInstanceState
true
, это может вызвать возможные утечки с вращением.
При использовании Fragment
с retainInstance
вы должны очистить все ваши ссылки ui в onDestroyView
@Override
public void onDestroyView() {
yourRecyclerView = null;
super.onDestroyView();
}
Здесь вы можете найти подробную информацию по этой ссылке:
Сохраненные фрагменты с UI и утечками памяти