Eu desenvolvi um aplicativo que usa muitas imagens no Android.
O aplicativo é executado uma vez, preenche a informação na tela ( Layouts
, Listviews
, Textviews
, ImageViews
, etc) e usuário lê as informações.
Não há animação, efeitos especiais ou qualquer coisa que possa preencher a memória. Às vezes, os drawables podem mudar. Alguns são recursos do Android e outros são arquivos salvos em uma pasta no SDCARD.
Em seguida, o usuário sai (o onDestroy
método é executado e o aplicativo permanece na memória pela VM) e, em algum momento, o usuário entra novamente.
Cada vez que o usuário entra no aplicativo, posso ver a memória crescendo cada vez mais até que o usuário obtenha o java.lang.OutOfMemoryError
.
Então, qual é a melhor / maneira correta de lidar com muitas imagens?
Devo colocá-los em métodos estáticos para que eles não sejam carregados o tempo todo? Preciso limpar o layout ou as imagens usadas no layout de uma maneira especial?
fonte
Respostas:
Parece que você tem um vazamento de memória. O problema não é lidar com muitas imagens, é que elas não são desalocadas quando sua atividade é destruída.
É difícil dizer por que isso ocorre sem olhar para o seu código. No entanto, este artigo tem algumas dicas que podem ajudar:
http://android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html
Em particular, o uso de variáveis estáticas provavelmente tornará as coisas piores, não melhores. Pode ser necessário adicionar código que remova retornos de chamada quando o aplicativo redesenhar - mas, novamente, não há informações suficientes aqui para garantir.
fonte
Um dos erros mais comuns que encontrei no desenvolvimento de aplicativos Android é o erro "java.lang.OutOfMemoryError: tamanho do bitmap excede o orçamento da VM". Encontrei esse erro frequentemente em atividades que usam muitos bitmaps após alterar a orientação: a atividade é destruída, criada novamente e os layouts são "inflados" a partir do XML, consumindo a memória da VM disponível para bitmaps.
Os bitmaps no layout de atividade anterior não são desalocados corretamente pelo coletor de lixo porque eles cruzaram as referências à sua atividade. Depois de muitas experiências, encontrei uma solução muito boa para esse problema.
Primeiro, defina o atributo "id" na visualização pai do seu layout XML:
Em seguida, no
onDestroy()
método da sua Atividade, chame ounbindDrawables()
método que passa uma referência para a Visualização pai e faça aSystem.gc()
.Este
unbindDrawables()
método explora a árvore de exibição recursivamente e:fonte
Para evitar esse problema, você pode usar método nativo
Bitmap.recycle()
antesnull
ingBitmap
objeto (ou estabelecendo outro valor). Exemplo:E a seguir, você pode alterar a
myBitmap
chamada semSystem.gc()
:fonte
Eu encontrei esse problema exato. A pilha é muito pequena, portanto essas imagens podem ficar fora de controle rapidamente em relação à memória. Uma maneira é dar ao coletor de lixo uma dica para coletar memória em um bitmap, chamando seu método de reciclagem .
Além disso, não é garantido que o método onDestroy seja chamado. Você pode mover essa lógica / limpeza para a atividade onPause. Confira o diagrama / tabela do Ciclo de Vida das Atividades nesta página para obter mais informações.
fonte
onPause
sugestão. Estou usando quatro guias comBitmap
imagens em todas elas, portanto, a destruição da atividade pode não acontecer tão cedo (se o usuário navegar em todas as guias).Esta explicação pode ajudar: http://code.google.com/p/android/issues/detail?id=8488#c80
"Dicas rápidas:
1) NUNCA ligue para System.gc (). Isso foi propagado como uma correção aqui e não funciona. Não faça isso. Se você notou na minha explicação, antes de obter um OutOfMemoryError, a JVM já executa uma coleta de lixo, portanto não há razão para fazer uma novamente (isso está atrasando o programa). Fazer uma no final de sua atividade está apenas encobrindo o problema. Isso pode fazer com que o bitmap seja colocado na fila do finalizador mais rapidamente, mas não há motivo para você não poder simplesmente chamar de reciclar em cada bitmap.
2) Sempre chame recycle () em bitmaps que você não precisa mais. No mínimo, na destruição de sua atividade, repasse e recicle todos os bitmaps que você estava usando. Além disso, se você quiser que as instâncias de bitmap sejam coletadas do heap do dalvik mais rapidamente, não será necessário limpar nenhuma referência ao bitmap.
3) Chamar recycle () e System.gc () ainda podem não remover o bitmap da pilha Dalvik. NÃO SE PREOCUPE com isso. O Recycle () fez seu trabalho e liberou a memória nativa. Levará algum tempo para seguir as etapas descritas anteriormente para remover o bitmap da pilha Dalvik. Isso NÃO é grande coisa porque a grande parte da memória nativa já está livre!
4) Sempre assuma que há um erro na estrutura por último. Dalvik está fazendo exatamente o que deveria fazer. Pode não ser o que você espera ou o que deseja, mas é como funciona. "
fonte
Eu tive o mesmo problema. Após alguns testes, descobri que esse erro está aparecendo para o dimensionamento de imagens grandes. Reduzi a escala da imagem e o problema desapareceu.
PS No começo, tentei reduzir o tamanho da imagem sem reduzi-la. Isso não parou o erro.
fonte
Os seguintes pontos realmente me ajudaram muito. Pode haver outros pontos também, mas estes são muito cruciais:
fonte
Sugiro uma maneira conveniente de resolver esse problema. Basta atribuir o valor do atributo "android: configChanges", conforme seguido no Mainfest.xml, para a sua atividade com erro. como isso:
a primeira solução apresentada realmente reduziu a frequência do erro OOM para um nível baixo. Mas, não resolveu o problema totalmente. E então eu vou dar a segunda solução:
Conforme detalhado no OOM, usei muita memória de tempo de execução. Portanto, reduzo o tamanho da imagem em ~ / res / drawable do meu projeto. Como uma imagem superqualificada com resolução de 128X128, pode ser redimensionada para 64x64, o que também seria adequado para a minha aplicação. E depois que fiz isso com uma pilha de fotos, o erro OOM não ocorre novamente.
fonte
Eu também estou frustrado com o bug fora da memória. E sim, também descobri que esse erro aparece muito ao dimensionar imagens. No começo, tentei criar tamanhos de imagem para todas as densidades, mas achei que isso aumentava substancialmente o tamanho do meu aplicativo. Então, agora estou usando apenas uma imagem para todas as densidades e dimensionando minhas imagens.
Meu aplicativo gerava um erro fora da memória sempre que o usuário passava de uma atividade para outra. Definir meus drawables como nulos e chamar System.gc () não funcionou, nem reciclar meus bitmapDrawables com getBitMap (). Recycle (). O Android continuaria lançando o erro fora da memória com a primeira abordagem e lançaria uma mensagem de erro de tela sempre que tentasse usar um bitmap reciclado com a segunda abordagem.
Fiz uma terceira abordagem. Defino todas as visualizações como nulas e o fundo como preto. Eu faço essa limpeza no meu método onStop (). Este é o método chamado assim que a atividade não estiver mais visível. Se você fizer isso no método onPause (), os usuários verão um fundo preto. Não é ideal. Quanto a fazê-lo no método onDestroy (), não há garantia de que será chamado.
Para impedir que uma tela preta ocorra se o usuário pressionar o botão Voltar no dispositivo, recarrego a atividade no método onRestart () chamando os métodos startActivity (getIntent ()) e depois concluo ().
Nota: não é realmente necessário alterar o fundo para preto.
fonte
Os métodos BitmapFactory.decode *, discutidos na lição Carregar bitmaps grandes com eficiência , não devem ser executados no thread principal da interface do usuário se os dados de origem forem lidos no disco ou em um local de rede (ou realmente em qualquer outra fonte que não seja a memória). O tempo que esses dados levam para carregar é imprevisível e depende de vários fatores (velocidade de leitura do disco ou da rede, tamanho da imagem, potência da CPU, etc.). Se uma dessas tarefas bloquear o encadeamento da interface do usuário, o sistema sinalizará seu aplicativo como não responsivo e o usuário terá a opção de fechá-lo (consulte Designing for Responsiveness para obter mais informações).
fonte
Bem, eu tentei tudo o que encontrei na internet e nenhum deles funcionou. Chamar System.gc () apenas reduz a velocidade do aplicativo. A reciclagem de bitmaps no onDestroy também não funcionou para mim.
A única coisa que funciona agora é ter uma lista estática de todo o bitmap para que os bitmaps sobrevivam após uma reinicialização. E use os bitmaps salvos em vez de criar novos sempre que a atividade for reiniciada.
No meu caso, o código fica assim:
fonte
Eu tive o mesmo problema apenas ao mudar as imagens de fundo com tamanhos razoáveis. Obtive melhores resultados ao definir o ImageView como nulo antes de colocar uma nova imagem.
fonte
FWIW, aqui está um cache de bitmap leve que eu codifiquei e usei por alguns meses. Não são todos os sinos e assobios, portanto, leia o código antes de usá-lo.
fonte