Ich habe eine Listenansicht mit ein paar Bildschaltflächen in jeder Zeile. Wenn Sie auf die Listenzeile klicken, wird eine neue Aktivität gestartet. Ich musste wegen eines Problems mit dem Kamera-Layout meine eigenen Registerkarten erstellen. Die Aktivität, die für das Ergebnis gestartet wird, ist eine Karte. Wenn ich auf meine Schaltfläche klicke, um die Bildvorschau zu starten (ein Bild von der SD-Karte laden), kehrt die Anwendung von der Aktivität zurück zur listview
Aktivität zum Ergebnishandler zurück, um meine neue Aktivität neu zu starten, die nichts anderes als ein Bild-Widget ist.
Die Bildvorschau in der Listenansicht erfolgt mit dem Cursor und ListAdapter
. Das macht es ziemlich einfach, aber ich bin mir nicht sicher, wie ich ein Bild mit geänderter Größe einfügen kann (dh kleinere Bitgröße, nicht Pixel wie src
für die Bildschaltfläche im laufenden Betrieb. Also habe ich nur die Größe des Bilds geändert, das von der Telefonkamera kam.
Das Problem ist, dass beim Versuch, die zweite Aktivität erneut zu starten, ein Fehler aufgrund von Speichermangel auftritt.
- Gibt es eine Möglichkeit, den Listenadapter einfach Zeile für Zeile zu erstellen, wobei ich die Größe im laufenden Betrieb ändern kann ( bitweise )?
Dies wäre vorzuziehen, da ich auch einige Änderungen an den Eigenschaften der Widgets / Elemente in jeder Zeile vornehmen muss, da ich aufgrund des Fokusproblems keine Zeile mit dem Touchscreen auswählen kann. ( Ich kann Rollerball verwenden. )
- Ich weiß, dass ich eine Out-of-Band-Größenänderung vornehmen und mein Bild speichern kann, aber das ist nicht wirklich das, was ich tun möchte, aber ein Beispielcode dafür wäre schön.
Sobald ich das Bild in der Listenansicht deaktiviert hatte, funktionierte es wieder einwandfrei.
Zu Ihrer Information: So habe ich es gemacht:
String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);
Wo R.id.imagefilename
ist ein ButtonImage
.
Hier ist mein LogCat:
01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed
Ich habe auch einen neuen Fehler beim Anzeigen eines Bildes:
01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri:
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
quelle
Antworten:
Die Android-Schulungsklasse " Bitmaps effizient anzeigen " bietet einige nützliche Informationen zum Verständnis und zum Umgang mit der Ausnahme
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
beim Laden von Bitmaps.Lesen Sie die Abmessungen und den Typ der Bitmap
Die
BitmapFactory
Klasse stellt mehrere Decodierungsverfahren (decodeByteArray()
,decodeFile()
,decodeResource()
, etc.) für eine Erstellung vonBitmap
aus verschiedenen Quellen. Wählen Sie die am besten geeignete Dekodierungsmethode basierend auf Ihrer Bilddatenquelle. Diese Methoden versuchen, Speicher für die erstellte Bitmap zuzuweisen, und können daher leicht zu einerOutOfMemory
Ausnahme führen. Jede Art von Dekodierungsmethode verfügt über zusätzliche Signaturen, mit denen Sie Dekodierungsoptionen über dieBitmapFactory.Options
Klasse angeben können . Wenn Sie dieinJustDecodeBounds
Eigenschafttrue
während des Decodierens auf setzen, wird die Speicherzuweisung vermieden, und es wirdnull
für das Bitmap-Objekt zurückgegeben, aber gesetztoutWidth
,outHeight
undoutMimeType
. Mit dieser Technik können Sie die Abmessungen und den Typ der Bilddaten vor der Erstellung (und Speicherzuweisung) der Bitmap lesen.Um
java.lang.OutOfMemory
Ausnahmen zu vermeiden , überprüfen Sie die Abmessungen einer Bitmap, bevor Sie sie dekodieren, es sei denn, Sie vertrauen absolut darauf, dass die Quelle Ihnen Bilddaten mit vorhersehbarer Größe liefert, die bequem in den verfügbaren Speicher passen.Laden Sie eine verkleinerte Version in den Speicher
Nachdem die Bildabmessungen bekannt sind, können sie verwendet werden, um zu entscheiden, ob das vollständige Bild in den Speicher geladen werden soll oder ob stattdessen eine unterabgetastete Version geladen werden soll. Hier sind einige Faktoren zu berücksichtigen:
Zum Beispiel lohnt es sich nicht, ein Bild mit 1024 x 768 Pixeln in den Speicher zu laden, wenn es irgendwann in einem Miniaturbild mit 128 x 96 Pixel in einem Bild angezeigt wird
ImageView
.Um den Decoder zu sagen , das Bild unterabzutasten, eine kleinere Version in dem Speicher, Satz Laden
inSampleSize
zutrue
in IhremBitmapFactory.Options
Objekt. Beispielsweise erzeugt ein Bild mit einer Auflösung von 2048 x 1536, das mit einer AuflösunginSampleSize
von 4 dekodiert wird, eine Bitmap von ungefähr 512 x 384. Das Laden in den Speicher verwendet 0,75 MB anstelle von 12 MB für das vollständige Image (unter der Annahme einer Bitmap-Konfiguration vonARGB_8888
). Hier ist eine Methode zum Berechnen eines Stichprobengrößenwerts, der eine Zweierpotenz basierend auf einer Zielbreite und -höhe ist:Um diese Methode zu verwenden, dekodieren Sie zuerst mit
inJustDecodeBounds
set totrue
, übergeben Sie die Optionen und dekodieren Sie dann erneut mit dem neueninSampleSize
Wert undinJustDecodeBounds
setzen Sie auffalse
:Diese Methode erleichtert das Laden einer Bitmap beliebig großer Größe in eine Bitmap mit
ImageView
einer Miniaturansicht von 100 x 100 Pixel, wie im folgenden Beispielcode gezeigt:Sie können einen ähnlichen Prozess ausführen, um Bitmaps aus anderen Quellen zu dekodieren, indem Sie die entsprechende
BitmapFactory.decode*
Methode nach Bedarf ersetzen .quelle
Um den OutOfMemory-Fehler zu beheben, sollten Sie Folgendes tun:
Diese
inSampleSize
Option reduziert den Speicherverbrauch.Hier ist eine vollständige Methode. Zuerst wird die Bildgröße gelesen, ohne den Inhalt selbst zu dekodieren. Dann findet es den besten
inSampleSize
Wert, es sollte eine Potenz von 2 sein, und schließlich wird das Bild dekodiert.quelle
Ich habe Fedors Code geringfügig verbessert. Es macht im Grunde das Gleiche, aber ohne die (meiner Meinung nach) hässliche while-Schleife und es ergibt sich immer eine Zweierpotenz. Ein großes Lob an Fedor für die ursprüngliche Lösung. Ich steckte fest, bis ich seine gefunden hatte, und dann konnte ich diese erstellen :)
quelle
BitmapFactory.decodeStream()
. Müssen Sie nicht für jeden einen Verweis speichern, damit sie in einemfinally
Block geschlossen werden können ?Ich komme aus der iOS-Erfahrung und war frustriert, ein Problem mit etwas so Grundlegendem wie dem Laden und Anzeigen eines Bildes zu entdecken. Schließlich versucht jeder, der dieses Problem hat, Bilder in angemessener Größe anzuzeigen. Wie auch immer, hier sind die beiden Änderungen, die mein Problem behoben haben (und meine App sehr reaktionsschnell gemacht haben).
1) Stellen Sie jedes Mal
BitmapFactory.decodeXYZ()
sicher, dass Sie einBitmapFactory.Options
withinPurgeable
set totrue
(und vorzugsweise with alsoinInputShareable
set totrue
) übergeben.2) NIEMALS verwenden
Bitmap.createBitmap(width, height, Config.ARGB_8888)
. Ich meine NIE! Ich hatte noch nie das Ding, das nach wenigen Durchgängen keinen Speicherfehler auslöste. Kein Betrag vonrecycle()
,System.gc()
, was auch immer geholfen. Es wurde immer eine Ausnahme ausgelöst. Die andere Möglichkeit, die tatsächlich funktioniert, besteht darin, ein Dummy-Bild in Ihren Drawables (oder einer anderen Bitmap, die Sie mit Schritt 1 oben dekodiert haben) zu haben, es nach Belieben neu zu skalieren und dann die resultierende Bitmap zu bearbeiten (z. B. es an eine Leinwand weiterzugeben) für mehr Spaß). Was Sie stattdessen verwenden sollten, ist :Bitmap.createScaledBitmap(srcBitmap, width, height, false)
. Wenn Sie aus irgendeinem Grund die Brute-Force-Erstellungsmethode verwenden MÜSSEN, müssen Sie mindestens bestehenConfig.ARGB_4444
.Dies spart Ihnen fast garantiert Stunden, wenn nicht Tage. Alles, was über das Skalieren des Bildes usw. spricht, funktioniert nicht wirklich (es sei denn, Sie halten eine falsche Größe oder ein verschlechtertes Bild für eine Lösung).
quelle
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true;
undBitmap.createScaledBitmap(srcBitmap, width, height, false);
löste mein Problem, das ich mit Speichermangel auf Android 4.0.0 hatte. Danke Kumpel!BitmapFactory.Options.inPurgeable
undBitmapFactory.Options.inInputShareable
veraltet. Developer.android.com/reference/android/graphics/…Es ist ein bekannter Fehler , nicht wegen großer Dateien. Da Android die Drawables zwischenspeichert, geht nach Verwendung weniger Bilder der Speicherplatz aus. Aber ich habe einen alternativen Weg dafür gefunden, indem ich das Android-Standard-Cache-System übersprungen habe.
Lösung : Verschieben Sie die Bilder in den Ordner "Assets" und verwenden Sie die folgende Funktion, um BitmapDrawable zu erhalten:
quelle
Ich hatte das gleiche Problem und löste es, indem ich die Funktionen BitmapFactory.decodeStream oder decodeFile vermeidete und stattdessen verwendete
BitmapFactory.decodeFileDescriptor
decodeFileDescriptor
Es sieht so aus, als würde es andere native Methoden als decodeStream / decodeFile aufrufen.Was auch immer funktioniert hat, war dies (beachten Sie, dass ich einige Optionen hinzugefügt habe, wie einige oben, aber das hat den Unterschied nicht ausgemacht. Entscheidend ist der Aufruf von BitmapFactory.decodeFileDescriptor anstelle von decodeStream oder decodeFile ):
Ich denke, es gibt ein Problem mit der nativen Funktion, die in decodeStream / decodeFile verwendet wird. Ich habe bestätigt, dass bei Verwendung von decodeFileDescriptor eine andere native Methode aufgerufen wird. Ich habe auch gelesen, "dass Bilder (Bitmaps) nicht auf Standard-Java-Weise, sondern über native Aufrufe zugewiesen werden; die Zuweisungen erfolgen außerhalb des virtuellen Heaps, werden aber dagegen gezählt! "
quelle
Ich denke, der beste Weg, dies zu vermeiden,
OutOfMemoryError
besteht darin, sich ihm zu stellen und ihn zu verstehen.Ich habe eine App erstellt, um absichtlich
OutOfMemoryError
die Speichernutzung zu verursachen und zu überwachen.Nachdem ich viele Experimente mit dieser App durchgeführt habe, habe ich die folgenden Schlussfolgerungen gezogen:
Ich werde zuerst vor Honey Comb über SDK-Versionen sprechen.
Bitmap wird auf einem nativen Heap gespeichert, aber der Müll wird automatisch gesammelt. Der Aufruf von recycle () ist unnötig.
Wenn {VM-Heap-Größe} + {zugewiesener nativer Heap-Speicher}> = {VM-Heap-Größenbeschränkung für das Gerät} und Sie versuchen, eine Bitmap zu erstellen, wird OOM ausgelöst.
HINWEIS: VM HEAP SIZE wird anstelle von VM ALLOCATED MEMORY gezählt.
Die Größe des VM-Heapspeichers wird nach dem Vergrößern niemals verkleinert, selbst wenn der zugewiesene VM-Speicher verkleinert wird.
Sie müssen also den maximalen VM-Speicher so niedrig wie möglich halten, damit die VM-Heap-Größe nicht zu groß wird, um verfügbaren Speicher für Bitmaps zu sparen.
Das manuelle Aufrufen von System.gc () ist bedeutungslos. Das System ruft es zuerst auf, bevor es versucht, die Größe des Heapspeichers zu erhöhen.
Die native Heap-Größe wird auch nie kleiner, aber sie wird nicht für OOM gezählt, sodass Sie sich darüber keine Sorgen machen müssen.
Lassen Sie uns dann über SDK Starts from Honey Comb sprechen.
Bitmap wird im VM-Heap gespeichert, nativer Speicher wird für OOM nicht gezählt.
Die Bedingung für OOM ist viel einfacher: {VM-Heap-Größe}> = {Beschränkung der VM-Heap-Größe für das Gerät}.
Sie haben also mehr verfügbaren Speicher, um eine Bitmap mit derselben Heap-Größenbeschränkung zu erstellen. Es ist weniger wahrscheinlich, dass OOM ausgelöst wird.
Hier sind einige meiner Beobachtungen zu Garbage Collection und Memory Leak.
Sie können es selbst in der App sehen. Wenn eine Aktivität eine AsyncTask ausgeführt hat, die nach der Zerstörung der Aktivität noch ausgeführt wurde, wird in der Aktivität erst nach Abschluss der AsyncTask Müll gesammelt.
Dies liegt daran, dass AsyncTask eine Instanz einer anonymen inneren Klasse ist und einen Verweis auf die Aktivität enthält.
Durch Aufrufen von AsyncTask.cancel (true) wird die Ausführung nicht gestoppt, wenn die Task in einer E / A-Operation im Hintergrundthread blockiert wird.
Rückrufe sind ebenfalls anonyme innere Klassen. Wenn eine statische Instanz in Ihrem Projekt sie enthält und sie nicht freigibt, geht Speicher verloren.
Wenn Sie eine sich wiederholende oder verzögerte Aufgabe, z. B. einen Timer, geplant haben und in onPause () nicht cancel () und purge () aufrufen, geht Speicher verloren.
quelle
private static class
in derselben Klasse. Sie werden keinen Hinweis auf die Aktivität enthalten (es sei denn, Sie geben ihnen natürlich eine)Ich habe in letzter Zeit viele Fragen zu OOM-Ausnahmen und Caching gesehen. Das Entwicklerhandbuch enthält einen wirklich guten Artikel dazu, aber einige scheitern daran, ihn auf geeignete Weise zu implementieren.
Aus diesem Grund habe ich eine Beispielanwendung geschrieben, die das Caching in einer Android-Umgebung demonstriert. Diese Implementierung hat noch keine OOM erhalten.
Am Ende dieser Antwort finden Sie einen Link zum Quellcode.
Bedarf:
Eigenschaften:
ListView
weg, wird es einfach nicht laden Sie die Bitmaps zwischenDies beinhaltet nicht:
Beispielcode:
Die Bilder, die heruntergeladen werden, sind Bilder (75 x 75) von Flickr. Geben Sie jedoch die Bild-URLs ein, die verarbeitet werden sollen, und die Anwendung verkleinert sie, wenn sie das Maximum überschreitet. In dieser Anwendung befinden sich die URLs einfach in einem
String
Array.Das
LruCache
hat eine gute Möglichkeit, mit Bitmaps umzugehen. In dieser Anwendung habe ich jedoch eine Instanz einerLruCache
anderen Cache-Klasse eingefügt, die ich erstellt habe, um die Anwendung praktikabler zu machen.Das kritische Zeug von Cache.java (die
loadBitmap()
Methode ist die wichtigste):Sie sollten nichts in der Datei Cache.java bearbeiten müssen, es sei denn, Sie möchten das Festplatten-Caching implementieren.
Die kritischen Dinge von MainActivity.java:
getView()
wird sehr oft angerufen. Es ist normalerweise keine gute Idee, Bilder dort herunterzuladen, wenn wir keine Überprüfung implementiert haben, die sicherstellt, dass wir nicht unendlich viele Threads pro Zeile starten. Cache.java prüft, ob sich dasrowObject.mBitmapUrl
bereits in einer Aufgabe befindet, und wenn dies der Fall ist, wird keine weitere gestartet. Daher überschreiten wir höchstwahrscheinlich nicht die Einschränkung der Arbeitswarteschlange aus demAsyncTask
Pool.Herunterladen:
Sie können den Quellcode von https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip herunterladen .
Letzte Worte:
Ich habe dies jetzt seit ein paar Wochen getestet, ich habe noch keine einzige OOM-Ausnahme erhalten. Ich habe dies auf dem Emulator, auf meinem Nexus One und auf meinem Nexus S getestet. Ich habe Bild-URLs getestet, die Bilder in HD-Qualität enthalten. Der einzige Engpass besteht darin, dass das Herunterladen länger dauert.
Es gibt nur ein mögliches Szenario, in dem ich mir vorstellen kann, dass das OOM angezeigt wird. Wenn wir viele wirklich große Bilder herunterladen und bevor sie skaliert und in den Cache gestellt werden, wird gleichzeitig mehr Speicherplatz beansprucht und ein OOM verursacht. Aber das ist sowieso nicht einmal eine ideale Situation und es wird höchstwahrscheinlich nicht möglich sein, sie auf eine praktikablere Weise zu lösen.
Fehler in den Kommentaren melden! :-)
quelle
Ich habe Folgendes getan, um das Bild aufzunehmen und die Größe im laufenden Betrieb zu ändern. Hoffe das hilft
quelle
Es scheint, dass dies ein sehr lang anhaltendes Problem ist, mit vielen unterschiedlichen Erklärungen. Ich habe den Rat der beiden am häufigsten vorgestellten Antworten befolgt, aber keine dieser Antworten hat meine Probleme gelöst, da die VM behauptete, sie könne sich die Bytes nicht leisten, um den Dekodierungsteil des Prozesses durchzuführen . Nach einigem Graben habe ich erfahren, dass das eigentliche Problem hier der Dekodierungsprozess ist, der vom NATIVE- Heap entfernt wird.
Siehe hier: BitmapFactory OOM macht mich verrückt
Das führte mich zu einem weiteren Diskussionsthread, in dem ich einige weitere Lösungen für dieses Problem fand. Eine Möglichkeit besteht darin,
System.gc();
manuell anzurufen, nachdem Ihr Bild angezeigt wurde. Dadurch verwendet Ihre App jedoch MEHR Speicher, um den nativen Heap zu reduzieren. Die bessere Lösung ab Version 2.0 (Donut) ist die Verwendung der BitmapFactory-Option "inPurgeable". Also habe ich einfacho2.inPurgeable=true;
gleich danach hinzugefügto2.inSampleSize=scale;
.Mehr zu diesem Thema hier: Ist die Grenze des Speicherheaps nur 6M?
Nachdem ich das alles gesagt habe, bin ich auch mit Java und Android ein Vollidiot. Wenn Sie also der Meinung sind, dass dies ein schrecklicher Weg ist, um dieses Problem zu lösen, haben Sie wahrscheinlich Recht. ;-) Aber das hat Wunder für mich gewirkt und ich fand es unmöglich, die VM jetzt aus dem Heap-Cache heraus laufen zu lassen. Der einzige Nachteil, den ich feststellen kann, ist, dass Sie Ihr zwischengespeichertes gezeichnetes Bild in den Papierkorb werfen. Das heißt, wenn Sie direkt zu diesem Bild zurückkehren, zeichnen Sie es jedes Mal neu. Wenn meine Anwendung funktioniert, ist das kein wirkliches Problem. Ihr Kilometerstand kann variieren.
quelle
Wenn keine der oben genannten Funktionen funktioniert, fügen Sie diese leider Ihrer Manifest- Datei hinzu. Innerhalb des Anwendungs- Tags
quelle
Verwenden Sie dies.
bitmap.recycle();
Dies hilft ohne Probleme mit der Bildqualität.quelle
Ich habe eine viel effektivere Lösung, die keinerlei Skalierung benötigt. Dekodieren Sie Ihre Bitmap einfach nur einmal und speichern Sie sie dann in einer Karte gegen ihren Namen. Rufen Sie dann einfach die Bitmap anhand des Namens ab und legen Sie sie in der ImageView fest. Es muss nichts mehr getan werden.
Dies funktioniert, da die tatsächlichen Binärdaten der decodierten Bitmap nicht im Dalvik-VM-Heap gespeichert sind. Es wird extern gespeichert. Jedes Mal, wenn Sie eine Bitmap dekodieren, wird Speicher außerhalb des VM-Heaps zugewiesen, der von GC niemals zurückgefordert wird
Stellen Sie sich vor, Sie haben Ihr Bild im Zeichenordner gespeichert, damit Sie dies besser einschätzen können. Sie erhalten das Bild einfach, indem Sie getResources (). GetDrwable (R.drawable.) Ausführen. Dadurch wird Ihr Image NICHT jedes Mal dekodiert, sondern jedes Mal, wenn Sie es aufrufen, wird eine bereits dekodierte Instanz erneut verwendet. Im Wesentlichen wird es also zwischengespeichert.
Da sich Ihr Image jetzt irgendwo in einer Datei befindet (oder möglicherweise sogar von einem externen Server stammt), liegt es in Ihrer Verantwortung, die decodierte Bitmap-Instanz zwischenzuspeichern, um sie dort wiederzuverwenden, wo sie benötigt wird.
Hoffe das hilft.
quelle
Ich habe das gleiche Problem auf folgende Weise gelöst.
quelle
Hier gibt es zwei Probleme ...
quelle
Das hat bei mir funktioniert!
quelle
Keine der oben genannten Antworten hat für mich funktioniert, aber ich habe eine schrecklich hässliche Problemumgehung gefunden, die das Problem gelöst hat. Ich habe meinem Projekt ein sehr kleines 1x1-Pixel-Bild als Ressource hinzugefügt und es in meine ImageView geladen, bevor ich die Garbage Collection aufgerufen habe. Ich denke, es könnte sein, dass ImageView die Bitmap nicht veröffentlicht hat, also hat GC sie nie aufgegriffen. Es ist hässlich, aber es scheint vorerst zu funktionieren.
quelle
bitmap
im obigen Code das vorherige bereits angezeigte Bild ist. Sie müssen es recyceln, alle Verweise darauf löschen und es auchimageView
vergessen, indem Sie einen winzigen Ersatzgc()
festlegen. und nach all dem: Laden Sie Ihr NEUES Bild in das oben angegebene Codebitmap
und zeigen Sie es an...
.Tolle Antworten hier, aber ich wollte eine voll nutzbare Klasse , um dieses Problem anzugehen. Also habe ich eine gemacht.
Hier ist meine BitmapHelper-Klasse , die OutOfMemoryError- geprüft ist :-)
quelle
Das funktioniert bei mir.
und das ist auf C # Monodroid. Sie können den Pfad des Bildes leicht ändern. Was hier wichtig ist, sind die einzustellenden Optionen.
quelle
Dies scheint der geeignete Ort zu sein, um meine Utility-Klasse zum Laden und Verarbeiten von Bildern für die Community freizugeben. Sie können sie gerne verwenden und frei ändern.
quelle
In einer meiner Bewerbungen muss ich entweder ein Bild von machen
Camera/Gallery
. Wenn der Benutzer auf ein Bild von der Kamera klickt (möglicherweise 2 MP, 5 MP oder 8 MP), variiert die Bildgröße vonkB
s bisMB
s. Wenn die Bildgröße kleiner (oder bis zu 1-2 MB) über dem Code liegt, funktioniert dies einwandfrei. Wenn ich jedoch ein Bild mit einer Größe über 4 MB oder 5 MB habe, wird der folgendeOOM
Frame angezeigt :(dann habe ich daran gearbeitet, dieses Problem zu lösen und schließlich habe ich die folgende Verbesserung des Fedor-Codes (All Credit to Fedor für die Erstellung einer so schönen Lösung) vorgenommen :)
Ich hoffe, das wird den Kumpels helfen, die vor dem gleichen Problem stehen!
Weitere Informationen finden Sie hier
quelle
Ich bin gerade vor ein paar Minuten auf dieses Problem gestoßen. Ich habe es gelöst, indem ich meinen Listenansichtsadapter besser verwaltet habe. Ich dachte, es sei ein Problem mit den Hunderten von 50x50px-Bildern, die ich verwendet habe. Es stellte sich heraus, dass ich jedes Mal, wenn die Zeile angezeigt wurde, versuchte, meine benutzerdefinierte Ansicht aufzublähen. Durch einfaches Testen, ob die Zeile aufgeblasen wurde, habe ich diesen Fehler beseitigt und verwende Hunderte von Bitmaps. Dies ist eigentlich für einen Spinner, aber der Basisadapter funktioniert für eine ListView trotzdem. Diese einfache Lösung hat auch die Leistung des Adapters erheblich verbessert.
quelle
Ich habe den ganzen Tag damit verbracht, diese Lösungen zu testen, und das einzige, was für mich funktioniert hat, sind die oben genannten Ansätze zum Abrufen des Bildes und zum manuellen Aufrufen des GC, von denen ich weiß, dass sie nicht erforderlich sein sollten, aber es ist das einzige, was funktioniert hat wenn ich meine App einem Schwerlasttest unterziehe, um zwischen Aktivitäten zu wechseln. Meine App verfügt über eine Liste von Miniaturbildern in einer Listenansicht in (z. B. Aktivität A). Wenn Sie auf eines dieser Bilder klicken, gelangen Sie zu einer anderen Aktivität (z. B. Aktivität B), in der ein Hauptbild für dieses Element angezeigt wird. Wenn ich zwischen den beiden Aktivitäten hin und her wechseln würde, würde ich schließlich den OOM-Fehler erhalten und die App würde das Schließen erzwingen.
Wenn ich auf halbem Weg in der Listenansicht wäre, würde es abstürzen.
Wenn ich jetzt das Folgende in Aktivität B implementiere, kann ich die gesamte Listenansicht ohne Probleme durchgehen und weitermachen und weitermachen und weitermachen ... und es ist schnell genug.
quelle
Dieses Problem tritt nur in Android-Emulatoren auf. Ich hatte dieses Problem auch in einem Emulator, aber als ich ein Gerät eincheckte, funktionierte es einwandfrei.
Also bitte ein Gerät einchecken. Es kann im Gerät ausgeführt werden.
quelle
Meine 2 Cent: Ich habe meine OOM-Fehler mit Bitmaps gelöst durch:
a) Skaliere meine Bilder um den Faktor 2
b) Verwenden der Picasso- Bibliothek in meinem benutzerdefinierten Adapter für eine ListView mit einem einzigen Aufruf in getView wie folgt:
Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);
quelle
Verwenden Sie diesen Code für jedes Bild, das in SdCard ausgewählt oder gezeichnet werden kann, um ein Bitmap-Objekt zu konvertieren.
Verwenden Sie Ihren Bildpfad instend von ImageData_Path.get (img_pos) .getPath () .
quelle
Im Allgemeinen beträgt die Heap-Größe des Android-Geräts nur 16 MB (variiert je nach Gerät / Betriebssystem, siehe Beitrag Heap-Größen ). Wenn Sie die Bilder laden und die Größe von 16 MB überschreiten, wird eine Speicherausnahme ausgelöst, anstatt die Bitmap zum Laden zu verwenden Bilder von der SD-Karte oder von Ressourcen oder sogar vom Netzwerk versuchen, getImageUri zu verwenden. Das Laden der Bitmap erfordert mehr Speicher, oder Sie können die Bitmap auf null setzen, wenn Sie mit dieser Bitmap arbeiten.
quelle
Für alle hier aufgeführten Lösungen muss IMAGE_MAX_SIZE festgelegt werden. Dies schränkt Geräte mit leistungsfähigerer Hardware ein. Wenn die Bildgröße zu niedrig ist, sieht sie auf dem HD-Bildschirm hässlich aus.
Ich habe eine Lösung herausgebracht, die mit meinem Samsung Galaxy S3 und mehreren anderen Geräten funktioniert, einschließlich weniger leistungsfähiger Geräte, mit besserer Bildqualität, wenn ein leistungsfähigeres Gerät verwendet wird.
Der Kern davon besteht darin, den maximalen Speicher zu berechnen, der für die App auf einem bestimmten Gerät zugewiesen ist, und dann die Skala so niedrig wie möglich einzustellen, ohne diesen Speicher zu überschreiten. Hier ist der Code:
Ich habe den maximalen Speicher, der von dieser Bitmap verwendet wird, auf 25% des maximal zugewiesenen Speichers festgelegt. Möglicherweise müssen Sie diesen an Ihre Bedürfnisse anpassen und sicherstellen, dass diese Bitmap bereinigt wird und nicht im Speicher bleibt, wenn Sie sie nicht mehr verwenden. Normalerweise verwende ich diesen Code, um eine Bildrotation durchzuführen (Quell- und Ziel-Bitmap), sodass meine App 2 Bitmaps gleichzeitig in den Speicher laden muss. 25% geben mir einen guten Puffer, ohne dass mir bei der Bildrotation der Speicher ausgeht.
Hoffe das hilft jemandem da draußen ..
quelle
Dies
OutofMemoryException
kann nicht vollständig gelöst werden, indem man dasSystem.gc()
und so weiter anruft .Unter Bezugnahme auf den AktivitätslebenszyklusDie Aktivitätszustände werden vom Betriebssystem selbst abhängig von der Speichernutzung für jeden Prozess und der Priorität jedes Prozesses bestimmt.
Sie können die Größe und die Auflösung für jedes der verwendeten Bitmap-Bilder berücksichtigen. Ich empfehle, die Größe zu reduzieren, auf niedrigere Auflösung neu abzutasten und sich auf das Design der Galerien zu beziehen (ein kleines Bild-PNG und ein Originalbild).
quelle
Dieser Code hilft beim Laden einer großen Bitmap aus Drawable
quelle
Bitmap.Config.ARGB_565
? Wenn hohe Qualität nicht kritisch ist.