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?

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.


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();

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.


+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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.