2016-02-20 3 views
17

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

enter image description here

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

Вот код для адаптера и использование класса решений этого: https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e

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

+0

Похоже, вы передаете контекст приложения, когда вам, вероятно, следует использовать контекст RecyclerView или контекст вашей деятельности. Контексты приложений - это долгая жизнь, которая предотвратила бы сбор. – Submersed

ответ

6

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

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

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

+0

Это то, что я закончил, и в деле исправляет проблему. Я видел этот ответ в другом вопросе. Я хотел бы понять, почему это так. Целевая цепочка должна быть Fragment -> Recyclerview -> Adapter. Итак, если фрагмент перестает ссылаться на Recyclerview, как recyclerview, так и его адаптер должны быть собраны в мусор. – feresr

+1

Когда вы устанавливаете адаптер для «RecyclerView» с «RecyclerView.setAdapter», этот адаптер будет регистрировать «RecyclerView» как «AdapterDataObserver» и содержать ссылку на него во внутреннем списке. Если адаптер живет дольше, чем 'RecyclerView' (в этом случае, если вы определяете адаптер в' onCreate' из 'Fragment', например), он будет содержать ссылку на« RecyclerView », который должен получать GC'ed вместо. Вызов 'recyclerView.setAdapter (null)' отменяет регистрацию 'RecyclerView' с этим адаптером. –

4

Прежде всего, Я ссылаюсь на this file.

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

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

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

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

+0

Хорошо спасибо !. Я пытаюсь это сделать прямо сейчас! Я не буду пропускать один и тот же экземпляр DayForecastAdapter на каждый HourlyViewHolder тоже – feresr

+0

Не повезло, те же результаты ... Если вы переключитесь на ветку «forecastadapter-leak», вы увидите изменения Я подал заявку. К сожалению, ошибка сохраняется. Спасибо за советы, хотя статичность VH - хорошая идея. – feresr

+1

Глядя на вашу ветку, я действительно думаю, что вы должны создать экземпляры каждого держателя DayForecastAdapter, а в строке 157 вместо labelViewHolder.recyclerView.setAdapter (dayForecastAdapter) сделать что-то вроде labelViewHolder.recyclerView.adapter.setData (...) который также должен вызвать метод notifyDataSetChanged() на адаптере. Поскольку ваш dayForecastAdapter является одним для каждого hourlyViewHolder, ForecastAdapter и dayForecastAdapter viewHolders связаны странным образом, может быть, это корень проблемы? – maciekjanusz

8

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

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

Этот вопрос вернулся в совершенно новом приложении, это лучший способ его решения? – feresr

+0

@feresr он никогда не исчезал :) Я все еще использую тот же «HackyRecyclerView», так как android 21 – Bolein95

+0

haha ​​приятно знать, что я не единственный, кто сталкивается с этой проблемой. В настоящее время я делаю onDestroyView() {recyclerView.adapter = null}. Это также устраняет проблему, но я хотел бы знать, что ее вызывает в первую очередь. – feresr

0

Я не могу открыть изображение и увидеть реальную утечку, но если определить локальную переменную для вашего RecyclerView в вашем Fragment и установить ваш осколок retainInstanceStatetrue это может вызвать возможные утечки с вращением.

При использовании Fragment с retainInstance вы должны очистить все ваши ссылки Ui в onDestroyView

@Override 
public void onDestroyView() { 
    yourRecyclerView = null; 
    super.onDestroyView(); 
} 

Здесь вы можете найти подробную информацию по этой ссылке: Retained Fragments with UI and memory leaks