Android: Comment fonctionne Bitmap recycle ()?

88

Disons que j'ai chargé une image dans un objet bitmap comme

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Maintenant, que se passera-t-il si je charge un autre bitmap comme

myBitmap = BitmapFactory.decodeFile(myFile2);

Que devient le premier myBitmap? Obtient-il Garbage Collected ou dois-je le ramasser manuellement avant de charger un autre bitmap, par exemple. myBitmap.recycle()?

De plus, y a-t-il un meilleur moyen de charger de grandes images et de les afficher l'une après l'autre tout en recyclant en cours de route?

Anuj Tenani
la source

Réponses:

22

Je pense que le problème est le suivant: sur les versions pré-Honeycomb d'Android, les données bitmap brutes réelles ne sont pas stockées dans la mémoire de la VM mais dans la mémoire native à la place. Cette mémoire native est libérée lorsque l' Bitmapobjet java correspondant est GC.

Cependant , lorsque vous manquez de mémoire native, le dalvik GC n'est pas déclenché, il est donc possible que votre application utilise très peu de mémoire java, de sorte que le dalvik GC n'est jamais appelé, mais il utilise des tonnes de mémoire native pour les bitmaps ce qui provoque finalement une erreur MOO.

Du moins, c'est mon avis. Heureusement dans Honeycomb et les versions ultérieures, toutes les données bitmap sont stockées dans la machine virtuelle, vous ne devriez donc pas avoir à les utiliser recycle()du tout. Mais pour les millions de 2,3 utilisateurs (la fragmentation secoue le poing ), vous devriez utiliser dans la recycle()mesure du possible (un problème énorme). Ou bien, vous pouvez appeler le GC à la place.

Timmmm
la source
21

Vous devrez appeler myBitmap.recycle () avant de charger l'image suivante.

Selon la source de votre myFile (par exemple, si c'est quelque chose que vous n'avez aucun contrôle sur la taille d'origine), lors du chargement d'une image au lieu de simplement rééchantillonner un nombre arbitraire, vous devez redimensionner l'image à la taille d'affichage.

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;

Je mets en cache displayWidth et displayHeight dans une statique que j'ai initialisée au début de mon activité.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
Djunod
la source
3
Vous n'avez pas besoin d'appeler recycle (), c'est juste une bonne idée si vous voulez libérer la mémoire tout de suite.
Karu
13
La réponse acceptée dit "Si vous voulez libérer de la mémoire dès que possible, vous devez appeler recycle ()". Votre réponse dit "Vous devrez appeler myBitmap.recycle ()". Il y a une différence entre "devrait" et "besoin de", et ce dernier est incorrect dans ce cas.
Karu
1
Le contexte est important. La question était "Y a-t-il également une meilleure façon de charger de grandes images et de les afficher l'une après l'autre en les recyclant en cours de route".
djunod
3
À partir d'Android 4.1, l'exemple ci-dessus peut ne pas fonctionner car createScaledBitmap peut renvoyer dans certains cas la même instance que l'original. Cela signifie que vous devez vérifier cet original! = MyBitmap avant de recycler l'original.
Jeremyfa
1
@Jeremyfa Il ne renvoie l'image d'origine que si vous spécifiez une largeur et une hauteur identiques à l'original. Dans ce cas, la mise à l'échelle est inutile, il pourrait donc aussi bien enregistrer certains processus en l'ignorant et en renvoyant l'image d'origine à la place. Il ne devrait rien "casser" cependant ...
Jabari
11

Une fois le bitmap chargé en mémoire, il était en fait composé de données en deux parties. La première partie comprend des informations sur le bitmap, une autre partie contient des informations sur les pixels du bitmap (il est composé d'un tableau d'octets). La première partie existe dans la mémoire utilisée Java, la seconde partie existe dans la mémoire utilisée C ++. Il peut utiliser directement la mémoire de l'autre. Bitmap.recycle () est utilisé pour libérer la mémoire de C ++. Si vous ne faites que cela, le GC collectera la partie de java et la mémoire de C sera toujours utilisée.

Allen
la source
+1 pour une manière intéressante mais très bonne de décrire pourquoi la mémoire n'est pas disponible pour GC immédiat - belle.
Richard Le Mesurier