O Navigation Arch Component poderia criar um vazamento de memória falso positivo?

14

Eu tenho um conhecimento básico de vazamentos de memória e o que pode causar a eles. É por isso que não entendo se tenho um problema no meu código ou se é um falso positivo. Não sei qual parte do código devo compartilhar, pois o projeto não é pequeno. Mas deixe-me saber nos comentários e adicionarei o código necessário.

Eu uso o componente do arco de navegação e sigo o padrão MVVM. Eu adicionei a biblioteca LeakCanary mais tarde no desenvolvimento do projeto e ela imediatamente começou a me dar avisos sobre instâncias retidas quando eu navegava pelas telas.

O problema ocorre quando adiciono fragmentos à pilha traseira. Com cada fragmento adicionado à pilha traseira, o contador de instâncias retidas aumenta. Quando atinge o valor limite de 5 LeakCanary despeja a pilha e fornece relatório.

Mas se eu clicar no botão Voltar e retornar às telas anteriores, o contador de instâncias retidas diminui e, eventualmente, quando retornadas à 1ª tela, todas as instâncias retidas desaparecem.

Se eu olhar para os relatórios de análise de heap, ele diz que a variável coordenatorLayout, que é uma referência ao CoordinatorLayoutXML, vazou. Se eu remover a variável e todo o seu uso e executar o aplicativo novamente, vejo o mesmo problema, mas agora com outra variável que é uma referência a outra exibição em xml. Tentei remover todas as visualizações e seu uso que o LeakCanary relatou como vazando. Quando ele disse que a TextView, que é usado apenas para definir um texto onViewCreatede não usado em nenhum outro lugar, está vazando, comecei a duvidar de que haja um problema no meu código.

Analisei as chamadas do método do ciclo de vida em fragmentos e notei que, quando navego para a nova tela do fragmento anterior, todos os métodos até e inclusive onDestroyViewsão chamados, mas não onDestroy. Quando clico em voltar, onDestroyé chamado o fragmento que estava no topo da pilha traseira e o contador de instâncias retidas diminui.

Suspeito que o componente Navigation esteja mantendo a instância de um fragmento quando estiver na pilha de trás e o LeakCanary o esteja vendo como um vazamento.

Marat
fonte

Respostas:

24

É assim que os Fragments na pilha traseira funcionam (e o Navigation apenas usa as APIs existentes do Fragment): a visualização do Fragment é destruída, mas o próprio Fragment não é destruído - eles são mantidos no CREATEDestado até você pressionar o botão voltar e retornar ao Fragment (após o qual onCreateView()será chamado novamente e você voltará para RESUMED).

De acordo com a conversa sobre Fragmentos: Passado, Presente e Futuro , uma das mudanças futuras que se aproximam dos Fragmentos é uma opção de opção para destruir Fragmentos na pilha traseira, em vez de ter dois ciclos de vida separados. Ainda não está disponível.

É necessário anular suas referências às visualizações, onDestroyViewpois esse é o sinal de que a exibição não está mais sendo usada pelo sistema Fragment e pode ser coletada com segurança se não for sua referência contínua à Visualização.

ianhanniballake
fonte
2
O Android View Binding resolve esse problema? Não consigo encontrar nenhuma documentação sobre se a referência aos modos de exibição View Binding (talvez o próprio objeto de ligação) é automaticamente 'anulada' onDestroyViewcom o View Binding.
Tim Malseed
3
@ TimMalseed - você precisa anular sua referência ao objeto de ligação, não há nada acontecendo automaticamente.
ianhanniballake
11
@Emmanuel - você precisa soltar sua referência ao próprio objeto de ligação, pois ele contém uma referência rígida às exibições que possui.
ianhanniballake
11
@Emmanuel - você sempre pode registrar uma solicitação de recurso !
ianhanniballake
11
@Emmanuel - Eu acho que certamente seria uma mudança de comportamento (o que pode implicar em uma opção de opção separada), mas ter o LifecycleOwner correto seria informação suficiente para corrigir uma série de problemas de memória.
ianhanniballake