2016-02-20 26 views
17

Zdecydowałem, że już najwyższy czas nauczyłem się używać Leak Canary do wykrywania wycieków w moich aplikacjach, i jak zawsze, starałem się zaimplementować to w moim projekcie, aby naprawdę zrozumieć, jak używać narzędzie. Wdrożenie tego było łatwe, trudną częścią było odczytanie tego, co narzędzie rzuca na mnie. Mam przewijania, które wydaje się gromadzić pamięć w menedżerze pamięci, jak przewijania w górę iw dół (nawet jeśli nie ładuje żadnych nowych danych), więc myślałem, że był to dobry obiekt kandydata do śledzenia wycieków, jest to wynik:Kanion wycieku, Recykling wycieku mAdapter

enter image description here

wygląda v7.widget.RecyclerView przecieka zasilacz, a nie mój wniosek. Ale to nie może być w porządku ... prawda?

Oto kod dla karty i korzystanie making klasa to: https://gist.github.com/feresr/a53c7b68145d6414c40ec70b3b842f1e

zacząłem bounty na to pytanie, ponieważ powrócił po dwóch latach w zupełnie innej aplikacji

+0

Wygląda na to, że przekazujesz kontekst aplikacji, gdy prawdopodobnie powinieneś użyć kontekstu RecyclerView lub kontekstu działań. Konteksty aplikacji są długotrwałe, co uniemożliwiłoby ich zbieranie. – Submersed

Odpowiedz

6

Jeśli adapter żyje dłużej niż RecyclerView ma, musisz wyczyścić odniesienie adaptera w onDestroyView:

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

W przeciwnym razie adapter będzie zawierał odniesienie do RecyclerView, które powinno już wyjść z pamięci.

+0

Tak właśnie zrobiłem, a w działaniu rozwiązuje problem. Widziałem tę samą odpowiedź w innym pytaniu dotyczącym SO. Chciałbym zrozumieć, dlaczego tak się dzieje. Łańcuchem odniesienia powinien być Fragment -> Recyclerview -> Adapter. Jeśli więc fragment przestaje odwoływać się do widoku Recycler, zarówno widok recyklingu, jak i jego adapter powinny zostać zebrane. – feresr

+1

Po ustawieniu adaptera dla 'RecyclerView' za pomocą' RecyclerView.setAdapter', adapter ten zarejestruje 'RecyclerView' jako' AdapterDataObserver' i zatrzyma odniesienie do niego na wewnętrznej liście. Jeśli adapter żyje dłużej niż 'RecyclerView' (co jest przypadkiem, jeśli zdefiniujesz na przykład adapter w' onCreate' fragmentu 'Fragment'), będzie on zawierał odniesienie do' RecyclerView', który powinien otrzymać GC'ed zamiast. Wywołanie 'recyclerView.setAdapter (null)' wyrejestrowuje 'RecyclerView' z tym adapterem. –

4

Przede wszystkim, Mam na myśli this file.

Wygląda jak v7.widget.RecyclerView wycieka z adaptera, a nie z mojej aplikacji. Ale to nie może być w porządku ... prawda?

To rzeczywiście karta że przecieka do RecyclerView (i to całkiem jasne, wykonane przez wykresu śladowych i tytule aktywności LeakCanary). Jednak nie jestem pewien, czy jest to "nadrzędny" RecyclerView lub zagnieżdżony w HourlyViewHolder, czy w obu. Myślę, że sprawcami są Twoje ViewHolders. Tworząc je niestatycznymi klasami wewnętrznymi, jawnie podajesz im odniesienie do otaczającej klasy adaptera, a to prawie bezpośrednio łączy adapter z widokami z recyklingu, ponieważ rodzicem każdego itemView w twoich uchwytach jest sam RecyclerView.

Moja pierwsza sugestia rozwiązania tego problemu polegałaby na oddzieleniu twoich ViewHolders i Adaptera, czyniąc je wewnętrznymi klasami. W ten sposób nie będą one odwoływać się do adaptera, więc twoje pole będzie dla nich niedostępne, a także dobrze, ponieważ odniesienia kontekstowe powinny być przekazywane i przechowywane oszczędnie (również w celu uniknięcia dużych wycieków pamięci). Gdy potrzebujesz kontekstu tylko w celu uzyskania łańcuchów, zrób to gdzie indziej, na przykład w konstruktorze adaptera, ale nie przechowuj kontekstu jako elementu. Wreszcie, DayForecastAdapter wydaje się niebezpieczny: przekazujesz jedno, to samo wystąpienie tego do każdego HourlyViewHolder, co wydaje się być błędem.

myślę, że ustalenie projektowania i oddzielenie tych klas należy pozbyć się tej pamięci wyciek

+0

Cóż, dziękuję !. Próbuję tego teraz !, unikam przekazywania tej samej instancji DayForecastAdapter na każdym HourlyViewHolder zbyt – feresr

+0

Bez powodzenia, te same wyniki ... Jeśli przełączysz się na gałąź'forecastadapter-leak 'zobaczysz zmiany Złożyłem wniosek. Niestety błąd nadal występuje. Dzięki za porady, dzięki czemu statyczny VH jest dobrym pomysłem. – feresr

+1

Patrząc na twoją gałąź, naprawdę powinieneś utworzyć instancje na jednego właściciela z DayForecastAdapter, a w linii 157 zamiast labelViewHolder.recyclerView.setAdapter (dayForecastAdapter) zrób coś jak labelViewHolder.recyclerView.adapter.setData (...) która powinna również wywoływać metodę notifyDataSetChanged() na adapterze. Ponieważ twój dayForecastAdapter jest jeden dla każdego hourlyViewHolder, viewHolder ForecastAdapter i dayHorecastAdapter's są sprzężone w dziwny sposób, może to jest przyczyną problemu? – maciekjanusz

8

udało mi się rozwiązać ten problem poprzez nadpisanie RecyclerView. Dzieje się tak, ponieważ RecyclerView nigdy nie wyrejestrowuje się z AdapterDataObservable.

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

Ten problem powrócił w zupełnie nowej aplikacji, czy to nadal najlepszy sposób na rozwiązanie tego problemu? – feresr

+0

@feresr nigdy nie odszedł :) Nadal używam tego samego 'HackyRecyclerView' od Androida 21 – Bolein95

+0

haha ​​dobrze wiedzieć, że nie jestem jedynym, który stoi w obliczu tego problemu. Obecnie robię naDestroyView() {recyclerView.adapter = null}. To również rozwiązuje problem, ale chciałbym wiedzieć, co powoduje to na pierwszym miejscu. – feresr

0

Nie mogę otworzyć swój obraz i zobaczyć rzeczywisty wyciek ale jeśli zdefiniować zmienną lokalną dla Twojego RecyclerView w Fragment i ustawić fragment na retainInstanceStatetrue może spowodować ewentualne przecieki z obrotu.

Używanie Fragment z retainInstance należy wyczyścić wszystkie swoje referencje ui w onDestroyView

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

Tutaj można znaleźć szczegółowe informacje z tego linku: Retained Fragments with UI and memory leaks