Android: Wie funktioniert Bitmap recycle ()?

88

Angenommen, ich habe ein Bild in ein Bitmap-Objekt wie geladen

Bitmap myBitmap = BitmapFactory.decodeFile(myFile);

Was passiert nun, wenn ich eine andere Bitmap wie lade?

myBitmap = BitmapFactory.decodeFile(myFile2);

Was passiert mit der ersten myBitmap? Wird es Garbage Collected oder muss ich es manuell sammeln, bevor ich eine andere Bitmap lade, z. myBitmap.recycle()?

Gibt es auch eine bessere Möglichkeit, große Bilder zu laden und nacheinander anzuzeigen, während Sie unterwegs recyceln?

Anuj Tenani
quelle

Antworten:

22

Ich denke, das Problem ist folgendes: In Vor-Honeycomb-Versionen von Android werden die tatsächlichen Bitmap-Rohdaten nicht im VM-Speicher, sondern im nativen Speicher gespeichert. Dieser native Speicher wird freigegeben, wenn das entsprechende Java- BitmapObjekt GC-fähig ist.

Allerdings , wenn Sie von nativem Speicher ausgeführt wird, wird die Dalvik GC nicht ausgelöst, so ist es möglich , dass Ihre App nutzt sehr wenig von der Java - Speicher, so der Dalvik GC wird nie aufgerufen, aber es Tonnen von nativem Speicher verwendet für Bitmaps was schließlich einen OOM-Fehler verursacht.

Zumindest ist das meine Vermutung. Zum Glück werden in Honeycomb und höher alle Bitmap-Daten in der VM gespeichert, sodass Sie sie überhaupt nicht verwenden müssen recycle(). Aber für die Millionen von 2,3 Benutzern (Fragmentierung schüttelt die Faust ) sollten Sie verwenden, recycle()wo immer dies möglich ist (ein massiver Aufwand). Alternativ können Sie stattdessen den GC aufrufen.

Timmmm
quelle
21

Sie müssen myBitmap.recycle () aufrufen, bevor Sie das nächste Bild laden.

Abhängig von der Quelle Ihrer myFile (z. B. wenn Sie keine Kontrolle über die Originalgröße haben) sollten Sie beim Laden eines Bildes, anstatt nur eine beliebige Zahl neu abzutasten, das Bild auf die Anzeigegröße skalieren.

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;

Ich speichere displayWidth & displayHeight in einer Statik, die ich zu Beginn meiner Aktivität initialisiert habe.

Display display = getWindowManager().getDefaultDisplay();
displayWidth = display.getWidth();
displayHeight = display.getHeight();
Djunod
quelle
3
Sie müssen recycle () nicht aufrufen, es ist nur eine gute Idee, wenn Sie den Speicher sofort freigeben möchten.
Karu
13
Die akzeptierte Antwort lautet "Wenn Sie so schnell wie möglich Speicher freigeben möchten, sollten Sie recycle () aufrufen". Ihre Antwort lautet "Sie müssen myBitmap.recycle () aufrufen". Es gibt einen Unterschied zwischen "sollte" und "muss", und letzteres ist in diesem Fall falsch.
Karu
1
Der Kontext ist wichtig. Die Frage lautete: "Gibt es auch eine bessere Möglichkeit, große Bilder zu laden und sie unterwegs nacheinander zu recyceln?"
Djunod
3
Ab Android 4.1 kann das obige Beispiel unterbrochen werden, da createScaledBitmap in einigen Fällen dieselbe Instanz wie die ursprüngliche zurückgeben kann. Das heißt, Sie müssen das Original überprüfen! = MyBitmap, bevor Sie das Original recyceln.
Jeremyfa
1
@Jeremyfa Das Originalbild wird nur zurückgegeben, wenn Sie eine Breite und Höhe angeben, die mit dem Original identisch ist. In diesem Fall ist die Skalierung umstritten. Sie können also auch einige Prozesse speichern, indem Sie sie überspringen und stattdessen das Originalbild zurückgeben. Es sollte aber nichts "brechen" ...
Jabari
11

Nachdem die Bitmap in den Speicher geladen worden war, wurde sie tatsächlich aus zweiteiligen Daten erstellt. Der erste Teil enthält einige Informationen über die Bitmap, der andere Teil enthält Informationen über die Pixel der Bitmap (sie besteht aus einem Byte-Array). Der erste Teil existiert im verwendeten Java-Speicher, der zweite Teil existiert im verwendeten C ++ - Speicher. Es kann den Speicher des anderen direkt verwenden. Bitmap.recycle () wird verwendet, um den Speicher von C ++ freizugeben. Wenn Sie dies nur tun, sammelt der GC den Teil von Java und der Speicher von C wird immer verwendet.

Allen
quelle
+1 für eine interessante, aber sehr gute Art zu beschreiben, warum der Speicher nicht für die sofortige GC verfügbar ist - eine schöne.
Richard Le Mesurier