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?

98voto

hp.android Punkte 2834

Einer der häufigsten Fehler, die ich bei der Entwicklung von Android-Apps gefunden habe, ist der "java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget" Fehler. Ich habe diesen Fehler häufig bei Aktivitäten gefunden, die viele Bitmaps verwenden, nachdem die Ausrichtung geändert wurde: Die Aktivität wird zerstört, neu erstellt und die Layouts werden aus dem XML "aufgeblasen", wobei der für Bitmaps verfügbare VM-Speicher verbraucht wird.

Bitmaps auf dem vorherigen Aktivitätslayout werden vom Garbage Collector nicht ordnungsgemäß freigegeben, da sie gekreuzte Referenzen zu ihrer Aktivität haben. Nach vielen Experimenten habe ich eine recht gute Lösung für dieses Problem gefunden.

Legen Sie zunächst das Attribut "id" für die übergeordnete Ansicht Ihres XML-Layouts fest:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Dann, auf der onDestroy() Methode Ihrer Aktivität, rufen Sie die unbindDrawables() Methode, die einen Verweis auf die übergeordnete Ansicht übergibt, und führen Sie dann eine System.gc() .

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

Diese unbindDrawables() Methode erkundet den Ansichtsbaum rekursiv und:

  1. Entfernt Rückrufe für alle Hintergrund-Drawables
  2. Entfernt Kinder in jeder Ansichtsgruppe

73voto

Trevor Johns Punkte 15484

Es klingt, als ob Sie ein Speicherleck haben. Das Problem ist nicht der Umgang mit vielen Bildern, es ist, dass Ihre Bilder nicht immer freigegeben, wenn Ihre Aktivität zerstört wird.

Es ist schwer zu sagen, woran das liegt, ohne sich Ihren Code anzusehen. Dieser Artikel enthält jedoch einige Tipps, die helfen könnten:

http://Android-developers.blogspot.de/2009/01/avoiding-memory-leaks.html

Insbesondere die Verwendung statischer Variablen wird die Situation eher verschlechtern als verbessern. Möglicherweise müssen Sie Code hinzufügen, der Rückrufe entfernt, wenn Ihre Anwendung neu gezeichnet wird - aber auch hier gibt es nicht genug Informationen, um das mit Sicherheit zu sagen.

11voto

ddmytrenko Punkte 788

Um dieses Problem zu vermeiden, können Sie die native Methode Bitmap.recycle() antes de null -ing Bitmap Objekt (oder das Setzen eines anderen Wertes). Beispiel:

public final void setMyBitmap(Bitmap bitmap) {
  if (this.myBitmap != null) {
    this.myBitmap.recycle();
  }
  this.myBitmap = bitmap;
}

Und als nächstes können Sie ändern myBitmap ohne Anruf System.gc() mögen:

setMyBitmap(null);    
setMyBitmap(anotherBitmap);

8voto

Donn Felker Punkte 9394

Ich bin genau auf dieses Problem gestoßen. Der Heap ist ziemlich klein, so dass diese Bilder können außer Kontrolle geraten ziemlich schnell in Bezug auf den Speicher. Eine Möglichkeit besteht darin, dem Garbage Collector einen Hinweis zu geben, Speicher für eine Bitmap zu sammeln, indem man sein Recycling-Verfahren .

Außerdem ist nicht garantiert, dass die onDestroy-Methode aufgerufen wird. Möglicherweise möchten Sie diese Logik/Aufräumarbeiten in die Aktivität onPause verschieben. Sehen Sie sich das Diagramm/die Tabelle des Aktivitätslebenszyklus an auf dieser Seite für weitere Informationen.

5voto

GSree Punkte 2882

Ich hatte genau das gleiche Problem. Nach einigen Tests fand ich heraus, dass dieser Fehler bei der Skalierung großer Bilder auftritt. Ich reduzierte die Bildskalierung und das Problem verschwand.

P.S. Zunächst habe ich versucht, die Bildgröße zu verringern, ohne das Bild zu skalieren. Das hat den Fehler nicht behoben.

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