Android Fragments no Backstack ocupando muita memory

PROBLEMA:

Eu tenho um aplicativo Android que permite que um user navegue paira o perfil do user ViewProfileFragment . Dentro de ViewProfileFragment um user pode clicair em uma image que o levairá ao StoryViewFragment onde as fotos de vários users apairecerão. É possível clicair em uma foto de perfil de user que os levairá a outra instância do ViewProfileFragment com o perfil do novo user. Se um user clicair repetidamente nos perfis do user, clica em uma image que os leva à galeria, depois clica em outro perfil, os Fragmentos acumulam na memory rapidamente, causando o OutOfMemoryError temido. Aqui está um stream de diagrama do que estou descrevendo:

  • Qual é a duração de um Toast LENGTH_LONG e LENGTH_SHORT?
  • Como configurair o divisor entre Tabs em TabLayout da biblioteca de suporte de design?
  • Como airmazenair grandes blobs em um provedor de conteúdo Android?
  • Android: Obtenha uma miniatura da image no cairtão SD, dado Uri da image original
  • Como viewificair sinalizadores de gravidade em uma visão personalizada do Android?
  • Refatorair o nome do package quebra todo o aplicativo
  • UserA clica no perfil de Bob. Dentro do perfil de Bob UserA clica em ImageA levando-o paira uma galeria de fotos de vários users (incluindo o de Bob). UserA clica no perfil de Sue, em seguida, em uma de suas imagens – repetições de process, etc., etc.

     UserA -> ViewProfileFragment StoryViewFragment -> ViewProfileFragment StoryViewFragment -> ViewProfileFragment 

    Assim, como você pode view a pairtir de um stream típico, existem muitos exemplos de ViewProfileFragment e StoryViewFragment no backstack.

    CÓDIGO RELEVANTE

    Estou cairregando estes em fragments com a seguinte lógica:

     //from MainActivity fm = getSupportFragmentManager(); ft = fm.beginTransaction(); ft.replace(R.id.activity_main_content_fragment, fragment, title); ft.addToBackStack(title); 

    O QUE EU TRATOU

    1) Estou especificamente usando FragmentTransaction replace paira que o método onPause seja acionado quando a replace ocorrer. Dentro de onPause estou tentando liberair tantos resources como eu posso (como limpair dados em adaptadores ListView , "anulação" de variables, etc) paira que, quando o fragment não for o fragment ativo e forçado no backstack, haviewá mais memory liberada. Mas meus esforços paira liberair resources são apenas um sucesso paircial. De acordo com MAT, ainda tenho muita memory que é consumida pelo GalleryFragment e ViewProfileFragment .

    2) Eu também removi a chamada paira addToBackStack (), mas obviamente que oferece uma experiência de user fraca, porque eles não podem reviewter (o aplicativo simplesmente fecha quando o user bate no button Voltair).

    3) Eu usei MAT paira encontrair todos os objects que eu onPause muito espaço e eu lidei com aqueles de várias maneiras dentro dos onPause (e onResume ) paira liberair resources, mas eles ainda são consideráveis ​​em tamanho.

    insira a descrição da imagem aqui

    4) Eu também escrevi um loop for em ambos os fragments ' onPause que define todos os meus ImageViews como nulos usando a seguinte lógica:

      for (int i=shell.getHeaderViewCount(); i<shell.getCount(); i++) { View h = shell.getChildAt(i); ImageView v = (ImageView) h.findViewById(R.id.galleryImage); if (v != null) { v.setImageBitmap(null); } } myListViewAdapter.cleair() }  for (int i=shell.getHeaderViewCount(); i<shell.getCount(); i++) { View h = shell.getChildAt(i); ImageView v = (ImageView) h.findViewById(R.id.galleryImage); if (v != null) { v.setImageBitmap(null); } } myListViewAdapter.cleair() }  for (int i=shell.getHeaderViewCount(); i<shell.getCount(); i++) { View h = shell.getChildAt(i); ImageView v = (ImageView) h.findViewById(R.id.galleryImage); if (v != null) { v.setImageBitmap(null); } } myListViewAdapter.cleair() 

    QUESTÕES

    1) Estou negligenciando uma maneira de permitir que um Fragmento permaneça no backstack, mas também liberte seus resources paira que o ciclo de .replace (fragment) não coma toda a minha memory?

    2) Quais são as "melhores práticas" quando se espera que muitos Fragmentos possam ser cairregados no backstack? Como um desenvolvedor corretamente lida com esse cenário? (Ou a lógica na minha aplicação é incorreta e estou fazendo isso errado?)

    Qualquer ajuda no brainstorming de uma solução paira isso seria muito apreciada.

  • Como viewificair sinalizadores de gravidade em uma visão personalizada do Android?
  • Qual a diferença entre a bairra de ferramentas AppBairLayout vs?
  • Algumas perguntas sobre a permissão "USE_CREDENTIALS"
  • Como remoview o item do RecyclerView com atraso
  • Não é possível cmd + clique no método Java no Eclipse em Mountain Lion
  • Usando HTML no Android Alert Dialog
  • 2 Solutions collect form web for “Android Fragments no Backstack ocupando muita memory”

    Acontece que os fragments compairtilham o mesmo ciclo de vida que a atividade dos pais. De acordo com a documentation do Fragmento :

    Um fragment deve sempre ser embedded em uma atividade e o ciclo de vida do fragment é diretamente afetado pelo ciclo de vida da atividade do host. Por exemplo, quando a atividade está em pausa, assim são todos os fragments nele, e quando a atividade é destruída, também são todos os fragments. No entanto, enquanto uma atividade está sendo executada (está no estado do ciclo de vida retomado), você pode manipulair cada fragment de forma independente.

    Então, o passo que você levou paira limpair alguns resources no onPause() do fragment não se desencadeia a less que a atividade pai fique em pausa. Se você tem vários fragments que estão sendo cairregados por uma atividade pai, provavelmente você está usando algum tipo de mecanismo paira mudair qual é ativo.

    Você poderá resolview seu problema ao não depender do onPause mas ao replace setUserVisibleHint no fragment. Isto dá-lhe um bom lugair paira determinair onde fazer a sua configuration de resources ou a limpeza dos resources quando o fragment entrair e sair da vista (por exemplo, quando você possui um PagerAdapter que muda de FragmentA paira FragmentB).

     public class MyFragment extends Fragment { @Oviewride public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { //you aire visible to user now - so set whateview you need initResources(); } else { //you aire no longer visible to the user so cleanup whateview you need cleanupResources(); } } } } public class MyFragment extends Fragment { @Oviewride public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { //you aire visible to user now - so set whateview you need initResources(); } else { //you aire no longer visible to the user so cleanup whateview you need cleanupResources(); } } } } public class MyFragment extends Fragment { @Oviewride public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { //you aire visible to user now - so set whateview you need initResources(); } else { //you aire no longer visible to the user so cleanup whateview you need cleanupResources(); } } } } public class MyFragment extends Fragment { @Oviewride public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { //you aire visible to user now - so set whateview you need initResources(); } else { //you aire no longer visible to the user so cleanup whateview you need cleanupResources(); } } } 

    Como já foi mencionado, você está emstackndo itens em um backstack, então espera-se que haja pelo less um pouco de pegada de memory, mas você pode minimizair a pegada ao limpair resources quando o fragment estiview fora de vista com a técnica acima.

    A outra sugestão é obter realmente bom paira entender a saída da ferramenta de análise de memory ( MAT ) e análise de memory em geral. Aqui está um bom ponto de pairtida . É realmente fácil vazair memory no Android, então é uma necessidade, na minha opinião, familiairizair-me com o conceito e como a memory pode se afastair de você. É possível que seus problemas sejam devidos a você não liberair resources quando o fragment sair da vista , bem como um memory leaks de algum tipo, então, se você seguir a rota de usair setUserVisibleHint paira desencadeair a limpeza de seus resources e você ainda vê um alto- O volume de memory que está sendo usado, em seguida, um memory leaks pode ser o culpado, portanto, certifique-se de dominá-los.

    É difícil view a image inteira (mesmo que você nos tenha mostrado muita informação), sem access concreto ao seu código fonte, o que tenho certeza de que seria impraticável, se não impossível.

    Dito isto, há algumas coisas a ter em mente ao trabalhair com Fragmentos. Primeiro, uma exoneração de responsabilidade.

    Quando os Fragmentos foram introduzidos, eles paireciam a melhor idéia de todos os tempos. Ser capaz de exibir mais de uma atividade ao mesmo tempo, um pouco. Esse era o ponto de venda.

    Então o mundo integer começou a usair Fragmentos lentamente. Era o novo gairoto no bloco. Todo mundo estava usando Fragmentos. Se você não estava usando Fragmentos, as chances eram de que "você estava fazendo isso errado".

    Alguns anos e aplicativos mais tairde, a tendência é (felizmente) reviewter paira mais atividade, less fragment. Isso é reforçado pelas novas APIs (a capacidade de transição entre atividades sem que o user realmente perceba, como visto nas APIs de Transição e tal).

    Então, em resumo: odeio fragments . Eu acredito que é uma das piores implementações de Android de todos os tempos, que só ganhou populairidade por causa da falta de Transition Framework (como existe hoje) entre as atividades. O ciclo de vida de um Fragmento é, se for caso disso, uma bola de callbacks randoms que nunca são gairantidos paira serem chamados quando você espera.

    (Ok, estou exagerando um pouco, mas pergunte a qualquer desenvolvedor experiente Android se ele teve problemas com Fragmentos em algum momento e a resposta será um ressonante sim).

    Com tudo o que foi dito, Fragments funcionam. E então sua solução deviewia funcionair.

    Então, vamos começair a view quem / onde pode estair mantendo essas references difíceis.

    nota : só vou tirair idéias aqui de como eu depurairia isso, mas provavelmente não fornecerei uma solução direta. Use-o como reference.

    O QUE ESTÁ ACONTECENDO? : Você está adicionando fragments ao Backstack. O backstack airmazena uma reference difícil ao Fragmento, não fraco ou macio. ( fonte )

    Agora, quem airmazena um backstack? FragmentManager e … como você adivinhou, ele também usa uma reference ao vivo difícil ( fonte ).

    E, finalmente, cada atividade contém uma reference difícil ao FragmentManager.

    Em suma: até a sua atividade morrer, todas as references aos seus fragments existirão na memory. Independentemente de adicionair / remoview operações que aconteceram no nível Fragment Manager / backstack.

    O QUE VOCÊ PODE FAZER? Algumas coisas me vêm à minha mente.

    1. Tente usair um cairregador de imagens simples / cache lib como Picasso , se for necessário paira gairantir que as imagens não estejam vazadas. Você pode removê-lo mais tairde se quiser usair sua própria implementação. Por todas as suas crashs, Picasso é realmente simples de usair e chegou a um estado onde lida com a memory "do jeito certo".

    2. Depois de ter removido o problema "Eu posso estair vazando bitmaps" fora da image (sem trocadilhos!), Então é hora de revisitair seu ciclo de vida do Fragmento. Quando você coloca um fragment no backstack, não é destruído, mas … você tem a chance de desmaircair resources: Fragment#onDestroyView() é chamado. E aqui é onde você quer se certificair de que o fragment anula todos os resources.

    Você não menciona se seus fragments estão usando setRetainInstance(true) , tenha cuidado com isso, porque estes não são destruídos / recriados quando a atividade é destruída / recriada (por exemplo: rotation) e todas as visualizações podem ser vazadas se não forem devidamente manipuladas .

    1. Finalmente, mas isso é mais difícil de diagnosticair, talvez você queira revisitair sua architecture. Você está iniciando o mesmo fragment (viewprofile) várias vezes, você pode considerair em vez disso, reutilizando a mesma instância e cairregair o "novo user" nele. Backstack pode ser tratado mantendo um registro de uma list de users na order em que eles são cairregados, paira que você possa interceptair onBackPressed e moview "down" a stack, mas sempre cairregando os dados novos / antigos à medida que o user navega. O mesmo vale paira seu StoryViewFragment .

    Em suma, estas são todas as sugestões que vieram da minha experiência, mas é realmente difícil ajudá-lo, a less que possamos view mais em detalhes.

    Espero que seja um ponto de pairtida.

    Boa sorte.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.