J'ai une connaissance de base des fuites de mémoire et de ce qui peut les provoquer. C'est pourquoi je ne comprends pas si j'ai un problème dans mon code ou s'il s'agit d'un faux positif. Je ne sais pas quelle partie du code je devrais partager car le projet n'est pas petit. Mais faites le moi savoir dans les commentaires et j'ajouterai le code requis.
J'utilise le composant de navigation Arch et suis le modèle MVVM. J'ai ajouté la bibliothèque LeakCanary plus tard dans le développement du projet et elle a immédiatement commencé à me donner des avertissements sur les instances conservées lorsque je navigue entre les écrans.
Le problème se produit lorsque j'ajoute des fragments à la pile arrière. Avec chaque fragment ajouté à la pile arrière, le compteur des instances conservées augmente. Lorsqu'il atteint la valeur seuil de 5, LeakCanary vide le tas et fournit un rapport.
Mais si je clique sur le bouton Retour et reviens aux écrans précédents, le compteur des instances retenues diminue et finalement, une fois retourné au 1er écran, toutes les instances retenues disparaissent.
Si je regarde les rapports d'analyse de tas, cela dit que la variable coordinatorLayout qui est une référence au CoordinatorLayout
en xml a fui. Si je supprime la variable et toute son utilisation et que je lance à nouveau l'application, je vois le même problème, mais maintenant avec une autre variable qui fait référence à une autre vue en xml. J'ai essayé de supprimer toutes les vues et leur utilisation que LeakCanary a signalées comme fuyant. Quand il a dit qu'un TextView
, qui est juste utilisé pour définir un texte dans onViewCreated
et qui n'est utilisé nulle part ailleurs, fuit, j'ai commencé à douter qu'il y ait un problème dans mon code.
J'ai analysé les appels de méthode du cycle de vie en fragments et j'ai remarqué que lorsque je navigue vers un nouvel écran pour le fragment précédent, toutes les méthodes jusqu'à et y compris onDestroyView
sont appelées mais pas onDestroy
. Lorsque je clique en arrière, onDestroy
est appelé pour un fragment qui était au-dessus de la pile arrière et le compteur d'instances conservées diminue.
Je soupçonne que le composant Navigation conserve l'instance d'un fragment lorsqu'il est dans la pile arrière et que LeakCanary le considère comme une fuite.
C'est ainsi que fonctionnent les fragments de la pile arrière (et la navigation utilise uniquement les API de fragment existantes): la vue du fragment est détruite, mais le fragment lui-même n'est pas détruit - ils sont conservés dans l'état CREATED
jusqu'à ce que vous frappiez l'arrière et revenez au Fragment (après quoi onCreateView()
sera appelé à nouveau et vous remonterez à RESUMED
).
Selon les Fragments: Past, Present et Future talk , l'un des futurs changements à venir aux Fragments est une option opt-in pour détruire les Fragments sur la pile arrière, plutôt que d'avoir deux cycles de vie séparés. Ce n'est pas encore disponible.
Vous devez annuler vos références aux vues dans onDestroyView
car c'est le signe que la vue n'est plus utilisée par le système Fragment et qu'elle peut être récupérée en toute sécurité si ce n'était pour votre référence continue à la vue.