Android: como funciona a reciclagem de bitmap ()?

88

Digamos que carreguei uma imagem em um objeto bitmap como

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Agora, o que acontecerá se eu carregar outro bitmap como

myBitmap = BitmapFactory.decodeFile(myFile2);

O que acontece com o primeiro myBitmap? Ele é coletado como lixo ou preciso coletá-lo manualmente antes de carregar outro bitmap, por exemplo. myBitmap.recycle()?

Além disso, existe uma maneira melhor de carregar imagens grandes e exibi-las uma após a outra enquanto faz a reciclagem pelo caminho?

Anuj Tenani
fonte

Respostas:

22

Acho que o problema é o seguinte: nas versões pré-Honeycomb do Android, os dados reais de bitmap não são armazenados na memória da VM, mas sim na memória nativa. Esta memória nativa é liberada quando o Bitmapobjeto java correspondente é GC'd.

No entanto , quando você fica sem memória nativa, o dalvik GC não é acionado, então é possível que seu aplicativo use muito pouco da memória java, então o dalvik GC nunca é invocado, mas usa toneladas de memória nativa para bitmaps que eventualmente causa um erro OOM.

Pelo menos é o meu palpite. Felizmente, no Honeycomb e em versões posteriores, todos os dados de bitmap são armazenados na VM, portanto, você não deve precisar usá-los recycle(). Mas para os milhões de 2,3 usuários (a fragmentação balança o punho ), você deve usar recycle()sempre que possível (um grande aborrecimento). Ou, alternativamente, você pode invocar o GC.

Timmmm
fonte
21

Você precisará chamar myBitmap.recycle () antes de carregar a próxima imagem.

Dependendo da fonte do seu myFile (por exemplo, se for algo que você não tem controle sobre o tamanho original), ao carregar uma imagem em vez de simplesmente reamostrar algum número arbitrário, você deve dimensionar a imagem para o tamanho de exibição.

if (myBitmap != null) {
    myBitmap.recycle();
    myBitmap = null;
}
Bitmap original = BitmapFactory.decodeFile(myFile);
myBitmap = Bitmap.createScaledBitmap(original, displayWidth, displayHeight, true);
if (original != myBitmap)
    original.recycle();
original = null;

Eu armazeno em cache displayWidth & displayHeight em uma estática que inicializei no início da minha atividade.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
Djunod
fonte
3
Você não precisa chamar recycle (), é apenas uma boa ideia se você quiser liberar a memória imediatamente.
Karu
13
A resposta aceita diz "Se você deseja liberar memória o mais rápido possível, deve chamar recycle ()". Sua resposta diz "Você precisará chamar myBitmap.recycle ()". Há uma diferença entre "deveria" e "preciso", e o último está incorreto neste caso.
Karu
1
O contexto é importante. A pergunta era "Também existe uma maneira melhor de carregar imagens grandes e exibi-las uma após a outra, reciclando no caminho".
djunod
3
A partir do Android 4.1, o exemplo acima pode falhar porque createScaledBitmap pode retornar, em alguns casos, a mesma instância do original. Isso significa que você deve verificar o original! = MyBitmap antes de reciclar o original.
Jeremyfa
1
@Jeremyfa Ele só retorna a imagem original se você especificar uma largura e altura que sejam idênticas ao original. Nesse caso, o dimensionamento é discutível, portanto, é melhor salvar alguns processos pulando-o e retornando a imagem original. Não deve "quebrar" nada ...
Jabari
11

Depois que o bitmap foi carregado na memória, na verdade ele foi feito por dados de duas partes. A primeira parte inclui algumas informações sobre o bitmap, a outra parte inclui informações sobre os pixels do bitmap (é composto por uma matriz de bytes). A primeira parte existe na memória usada Java, a segunda parte existe na memória usada C ++. Eles podem usar a memória um do outro diretamente. Bitmap.recycle () é usado para liberar a memória de C ++. Se você apenas fizer isso, o GC irá coletar a parte de java e a memória de C será sempre usada.

Allen
fonte
1 para uma maneira interessante, mas muito boa de descrever porque a memória não está disponível para GC imediato - uma boa maneira.
Richard Le Mesurier
8

Timmmm estava certo.

de acordo com: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

Além disso, antes do Android 3.0 (API de nível 11), os dados de apoio de um bitmap eram armazenados na memória nativa que não era liberada de maneira previsível, potencialmente fazendo com que um aplicativo excedesse brevemente seus limites de memória e travasse.

Jian
fonte