160 Stimmen

java.lang.OutOfMemoryError: Bitmap-Größe überschreitet VM-Budget - Android

Ich habe eine Anwendung entwickelt, die viele Bilder auf Android verwendet.

Die App läuft einmal, füllt die Informationen auf dem Bildschirm ( Layouts , Listviews , Textviews , ImageViews usw.) und der Benutzer liest die Informationen.

Es gibt keine Animationen, keine Spezialeffekte oder andere Dinge, die den Speicher füllen können. Manchmal können sich die Zeichenobjekte ändern. Einige sind Android-Ressourcen und einige sind Dateien, die in einem Ordner auf der SDCARD gespeichert sind.

Dann verlässt der Benutzer (die onDestroy Methode ausgeführt wird und die App im Speicher der VM verbleibt) und dann irgendwann der Benutzer wieder eintritt.

Jedes Mal, wenn der Benutzer die Anwendung aufruft, kann ich sehen, wie der Speicher immer mehr wächst, bis der Benutzer die java.lang.OutOfMemoryError .

Was ist also der beste/richtige Weg, um mit vielen Bildern umzugehen?

Sollte ich sie in statische Methoden setzen, so dass sie nicht die ganze Zeit geladen werden? Muss ich das Layout oder die Bilder, die im Layout verwendet werden, auf eine besondere Weise reinigen?

5voto

Die folgenden Punkte haben mir wirklich sehr geholfen. Vielleicht gibt es auch noch andere Punkte, aber diese sind sehr wichtig:

  1. Verwenden Sie den Anwendungskontext (anstelle von activity.this), wo immer dies möglich ist.
  2. Anhalten und Freigeben der Threads in der onPause()-Methode der Aktivität
  3. Geben Sie Ihre Ansichten/Callbacks in der onDestroy()-Methode der Aktivität frei

4voto

Ranger Way Punkte 529

Ich schlage einen bequemen Weg vor, dieses Problem zu lösen. Weisen Sie einfach das Attribut "Android:configChanges" mit folgendem Wert in der Mainfest.xml für Ihre fehlerhafte Aktivität zu. etwa so:

<activity android:name=".main.MainActivity"
              android:label="mainActivity"
              android:configChanges="orientation|keyboardHidden|navigation">
</activity>

Die erste Lösung, die ich vorstellte, hatte die Häufigkeit von OOM-Fehlern wirklich auf ein niedriges Niveau reduziert. Aber sie hat das Problem nicht vollständig gelöst. Und dann werde ich die 2. Lösung vorstellen:

Wie im OOM beschrieben, habe ich zu viel Laufzeitspeicher verwendet. Also reduziere ich die Bildgröße in ~/res/drawable meines Projekts. Ein überqualifiziertes Bild, das eine Auflösung von 128X128 hat, könnte auf 64x64 verkleinert werden, was auch für meine Anwendung geeignet wäre. Und nachdem ich dies mit einem Stapel von Bildern getan habe, tritt der OOM-Fehler nicht mehr auf.

3voto

subduedjoy Punkte 41

Auch ich bin frustriert über den Out-of-Memory-Bug. Und ja, auch ich habe festgestellt, dass dieser Fehler häufig beim Skalieren von Bildern auftaucht. Zuerst habe ich versucht, Bildgrößen für alle Dichten zu erstellen, aber ich fand, dass dies die Größe meiner Anwendung erheblich erhöht. Also verwende ich jetzt nur noch ein Bild für alle Dichten und skaliere meine Bilder.

Meine Anwendung würde einen Outofmemory-Fehler auslösen, wenn der Benutzer von einer Aktivität zu einer anderen wechselt. Meine Drawables auf null zu setzen und System.gc() aufzurufen hat nicht funktioniert, genauso wenig wie das Recyceln meiner BitmapDrawables mit getBitMap().recycle(). Android würde weiterhin den outofmemory-Fehler mit dem ersten Ansatz werfen, und es würde eine Canvas-Fehlermeldung werfen, wann immer es versuchte, eine recycelte Bitmap mit dem zweiten Ansatz zu verwenden.

Ich habe sogar einen dritten Ansatz gewählt. Ich setzte alle Ansichten auf Null und den Hintergrund auf Schwarz. Ich mache diese Bereinigung in meiner onStop()-Methode. Dies ist die Methode, die aufgerufen wird, sobald die Aktivität nicht mehr sichtbar ist. Wenn Sie dies in der onPause()-Methode tun, sehen die Benutzer einen schwarzen Hintergrund. Das ist nicht ideal. Wenn Sie es in der onDestroy()-Methode machen, gibt es keine Garantie, dass sie aufgerufen wird.

Um zu verhindern, dass ein schwarzer Bildschirm angezeigt wird, wenn der Benutzer die Zurück-Taste auf dem Gerät drückt, lade ich die Aktivität in der onRestart()-Methode neu, indem ich die Methoden startActivity(getIntent()) und finish() aufrufe.

Hinweis: Es ist nicht unbedingt notwendig, den Hintergrund auf schwarz zu setzen.

1voto

Li3ro Punkte 1779

Die BitmapFactory.decode*-Methoden, die im Abschnitt Effizientes Laden großer Bitmaps im Unterricht sollte nicht im Haupt-UI-Thread ausgeführt werden, wenn die Quelldaten von der Festplatte oder aus dem Netz gelesen werden (oder aus einer anderen Quelle als dem Speicher). Die Zeit, die diese Daten zum Laden benötigen, ist nicht vorhersehbar und hängt von einer Vielzahl von Faktoren ab (Geschwindigkeit des Lesens von der Festplatte oder vom Netzwerk, Größe des Bildes, Leistung der CPU usw.). Wenn eine dieser Aufgaben den UI-Thread blockiert, kennzeichnet das System Ihre Anwendung als nicht reaktionsfähig und der Benutzer hat die Möglichkeit, sie zu schließen (weitere Informationen finden Sie unter Designing for Responsiveness).

0voto

Gunnar Bernstein Punkte 5786

Ich hatte das gleiche Problem, nur mit dem Wechsel der Hintergrundbilder in vernünftigen Größen. Bessere Ergebnisse erzielte ich, indem ich die ImageView auf null setzte, bevor ich ein neues Bild einfügte.

ImageView ivBg = (ImageView) findViewById(R.id.main_backgroundImage);
ivBg.setImageDrawable(null);
ivBg.setImageDrawable(getResources().getDrawable(R.drawable.new_picture));

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X