Czy komponent Navigation Arch może spowodować fałszywie dodatni wyciek pamięci?

14

Mam podstawową wiedzę na temat wycieków pamięci i ich przyczyn. Dlatego nie rozumiem, czy mam problem z kodem, czy jest to fałszywy alarm. Nie wiem, którą część kodu powinienem udostępnić, ponieważ projekt nie jest mały. Ale daj mi znać w komentarzach, a dodam wymagany kod.

Używam elementu arch nawigacji i kieruję się wzorem MVVM. Później dodałem bibliotekę LeakCanary podczas opracowywania projektu i od razu zaczęła ostrzegać mnie o zachowanych instancjach podczas nawigacji między ekranami.

Problem występuje, gdy dodam fragmenty do tylnego stosu. Z każdym dodanym fragmentem do tylnego stosu wzrasta liczba zachowanych instancji. Kiedy osiągnie wartość progową 5 LeakCanary, zrzuca stos i dostarcza raport.

Ale jeśli kliknę przycisk Wstecz i wrócę do poprzednich ekranów, licznik zachowanych wystąpień zmniejszy się, a ostatecznie, po powrocie do pierwszego ekranu wszystkie zachowane wystąpienia znikną.

Jeśli spojrzę na raporty analizy sterty, to mówi, że CoordinatorLayoutwyciekła zmienna koordynatorLayout, która jest odniesieniem do pliku xml. Jeśli usunę zmienną i całe jej użycie i ponownie uruchomię aplikację, widzę ten sam problem, ale teraz z inną zmienną, która jest odniesieniem do innego widoku w xml. Próbowałem usunąć wszystkie widoki i ich użycie, które LeakCanary zgłosiło jako nieszczelne. Kiedy powiedziano, że przeciek TextView, który jest po prostu używany do wpisania tekstu onViewCreatedi nie jest używany nigdzie indziej, przecieka, zacząłem wątpić, czy w moim kodzie jest problem.

Analizowałem wywołania metod cyklu życia we fragmentach i zauważyłem, że kiedy przechodzę do nowego ekranu poprzedniego fragmentu, wszystkie metody onDestroyViewsą wywoływane, ale nie są wywoływane onDestroy. Kiedy klikam wstecz, onDestroywywoływany jest fragment, który był na wierzchu stosu, a zachowane wystąpienia licznika zmniejszają się.

Podejrzewam, że komponent Nawigacji zachowuje instancję fragmentu, gdy znajduje się w tylnym stosie, a LeakCanary postrzega go jako wyciek.

Marat
źródło

Odpowiedzi:

24

Tak działają Fragmenty na tylnym stosie (a Nawigacja korzysta tylko z istniejących interfejsów API Fragmentów): widok Fragmentu jest zniszczony, ale sam Fragment nie jest zniszczony - są one utrzymywane w CREATEDstanie do momentu naciśnięcia przycisku Wstecz i powrotu do Fragmentu (po czym onCreateView()zostanie ponownie wywołany, a ty wrócisz do RESUMED).

Zgodnie z rozmową Fragmenty: Przeszłość, Obecność i Przyszłość , jedna z przyszłych zmian nadchodzących do Fragmentów to opcja niszczenia Fragmentów z tyłu stosu, zamiast posiadania dwóch osobnych cykli życia. To nie jest jeszcze dostępne.

Musisz wyzerować swoje odniesienia do widoków, onDestroyViewponieważ jest to znak, że widok nie jest już używany przez system Fragmentów i może być bezpiecznie wyrzucony, jeśli nie byłoby to dla twojego dalszego odniesienia do Widoku.

ianhanniballake
źródło
2
Czy wiązanie widoku systemu Android rozwiązuje ten problem? Nie mogę znaleźć żadnej dokumentacji dotyczącej tego, czy odwołanie do widoków Powiązanie widoku (może sam obiekt powiązania) jest automatycznie „zerowane” w onDestroyViewPowiązaniu View.
Tim Malseed
3
@TimMalseed - musisz samodzielnie usunąć odwołanie do obiektu wiążącego, nic się nie dzieje automatycznie.
ianhanniballake
1
@Emmanuel - musisz upuścić swoje odwołanie do samego obiektu wiążącego, ponieważ zawiera ono twarde odwołanie do posiadanych widoków.
ianhanniballake
1
@Emmanuel - zawsze możesz złożyć wniosek o funkcję !
ianhanniballake
1
@Emmanuel - Myślę, że z pewnością byłaby to zmiana w zachowaniu (co może oznaczać, że jest to osobna flaga opt in), ale posiadanie prawidłowego LifecycleOwner byłoby wystarczającą informacją, aby naprawić cały szereg problemów z pamięcią.
ianhanniballake