Obwohl ich ein sehr kleines Bild im Drawable-Ordner habe, erhalte ich diesen Fehler von Benutzern. Und ich verwende keine Bitmap-Funktion im Code. Zumindest gewollt 🙂
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:683)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:513)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:889)
at android.content.res.Resources.loadDrawable(Resources.java:3436)
at android.content.res.Resources.getDrawable(Resources.java:1909)
at android.view.View.setBackgroundResource(View.java:16251)
at com.autkusoytas.bilbakalim.SoruEkrani.cevapSecimi(SoruEkrani.java:666)
at com.autkusoytas.bilbakalim.SoruEkrani$9$1.run(SoruEkrani.java:862)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:146)
at android.app.ActivityThread.main(ActivityThread.java:5602)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1283)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
at dalvik.system.NativeStart.main(Native Method)
Laut diesem StackTrace erhalte ich diesen Fehler in dieser Zeile (‘tv’ ist eine TextView):
tv.setBackgroundResource(R.drawable.yanlis);
Was ist das Problem? Wenn Sie weitere Informationen zum Code benötigen, kann ich diese hinzufügen. Danke!
Sie können die Heap-Größe nicht dynamisch erhöhen, aber Sie können anfordern, mehr zu verwenden, indem Sie verwenden.
android:largeHeap=”true”
in dem manifest.xml
können Sie diese Zeilen in Ihr Manifest einfügen, es funktioniert in einigen Situationen.
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
Ob die Prozesse Ihrer Anwendung mit einem großen Dalvik-Heap erstellt werden sollen. Dies gilt für alle Prozesse, die für die Anwendung erstellt werden. Es gilt nur für die erste Anwendung, die in einen Prozess geladen wird; Wenn Sie eine gemeinsame Benutzer-ID verwenden, um mehreren Anwendungen die Verwendung eines Prozesses zu ermöglichen, müssen alle diese Option konsistent verwenden, da sie sonst zu unvorhersehbaren Ergebnissen führen. Die meisten Apps sollten dies nicht benötigen und sich stattdessen darauf konzentrieren, ihre Gesamtspeichernutzung zu reduzieren, um die Leistung zu verbessern. Die Aktivierung garantiert auch keine feste Erhöhung des verfügbaren Speichers, da einige Geräte durch ihren insgesamt verfügbaren Speicher eingeschränkt sind.
Um die verfügbare Speichergröße zur Laufzeit abzufragen, verwenden Sie die Methoden getMemoryClass()
oder getLargeMemoryClass()
.
Wenn immer noch ein Problem auftritt, sollte dies auch funktionieren
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);
Wenn auf einen Wert > 1 gesetzt, wird der Decoder aufgefordert, das Originalbild zu unterabtasten und ein kleineres Bild zurückzugeben, um Speicherplatz zu sparen.
Dies ist die optimale Verwendung von BitmapFactory.Options.inSampleSize im Hinblick auf die Anzeigegeschwindigkeit des Bildes. Die Dokumentation erwähnt die Verwendung von Werten, die eine Potenz von 2 sind, also arbeite ich mit 2, 4, 8, 16 usw.
Lassen Sie uns tiefer in das Bild-Sampling einsteigen:
Es lohnt sich beispielsweise nicht, ein 1024 x 768 Pixel großes Bild in den Speicher zu laden, wenn es schließlich in einem 128 x 128 Pixel großen Thumbnail in einem angezeigt wird ImageView
.
Um den Decoder anzuweisen, das Bild zu unterabtasten, indem eine kleinere Version in den Speicher geladen wird, set inSampleSize
zu true
in deiner BitmapFactory.Options
Objekt. Zum Beispiel ein Bild mit einer Auflösung von 2100 x 1500 Pixel, das mit dekodiert wird inSampleSize
von 4 erzeugt eine Bitmap von ungefähr 512 x 384. Das Laden in den Speicher verwendet 0,75 MB statt 12 MB für das vollständige Bild (unter der Annahme einer Bitmap-Konfiguration von ARGB_8888
). Hier ist eine Methode zum Berechnen eines Stichprobengrößenwerts, der eine Potenz von zwei ist, basierend auf einer Zielbreite und -höhe:
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
Notiz: Ein Zweierpotenzwert wird berechnet, da der Decoder einen Endwert verwendet, indem er gemäß der auf die nächste Zweierpotenz abrundet
inSampleSize
Dokumentation.
Um diese Methode zu verwenden, decodieren Sie zuerst mit inJustDecodeBounds
einstellen true
übergeben Sie die Optionen und dekodieren Sie dann erneut mit dem neuen inSampleSize
Wert und inJustDecodeBounds
einstellen false
:
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
Diese Methode erleichtert das Laden einer beliebig großen Bitmap in eine ImageView
das ein 100 x 100 Pixel großes Thumbnail anzeigt, wie im folgenden Beispielcode gezeigt:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
Sie können einem ähnlichen Prozess folgen, um Bitmaps aus anderen Quellen zu decodieren, indem Sie das entsprechende ersetzen BitmapFactory.decode*
Methode nach Bedarf.
Ich fand diesen Code auch interessant:
private Bitmap getBitmap(String path) {
Uri uri = getImageUri(path);
InputStream in = null;
try {
final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
in = mContentResolver.openInputStream(uri);
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in, null, o);
in.close();
int scale = 1;
while ((o.outWidth * o.outHeight) * (1 / Math.pow(scale, 2)) >
IMAGE_MAX_SIZE) {
scale++;
}
Log.d(TAG, "scale = " + scale + ", orig-width: " + o.outWidth + ",
orig-height: " + o.outHeight);
Bitmap bitmap = null;
in = mContentResolver.openInputStream(uri);
if (scale > 1) {
scale--;
// scale to max possible inSampleSize that still yields an image
// larger than target
o = new BitmapFactory.Options();
o.inSampleSize = scale;
bitmap = BitmapFactory.decodeStream(in, null, o);
// resize to desired dimensions
int height = bitmap.getHeight();
int width = bitmap.getWidth();
Log.d(TAG, "1th scale operation dimenions - width: " + width + ",
height: " + height);
double y = Math.sqrt(IMAGE_MAX_SIZE
/ (((double) width) / height));
double x = (y / height) * width;
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bitmap, (int) x,
(int) y, true);
bitmap.recycle();
bitmap = scaledBitmap;
System.gc();
} else {
bitmap = BitmapFactory.decodeStream(in);
}
in.close();
Log.d(TAG, "bitmap size - width: " +bitmap.getWidth() + ", height: " +
bitmap.getHeight());
return bitmap;
} catch (IOException e) {
Log.e(TAG, e.getMessage(),e);
return null;
}
So verwalten Sie den Speicher Ihrer App: Verknüpfung
Es ist keine gute Idee, es zu verwenden android:largeHeap="true"
Hier ist der Auszug von Google, der es erklärt,
Die Möglichkeit, einen großen Heap anzufordern, ist jedoch nur für eine kleine Gruppe von Apps gedacht, die die Notwendigkeit rechtfertigen können, mehr RAM zu verbrauchen (z. B. eine große Fotobearbeitungs-App). Fordern Sie niemals einen großen Heap an, nur weil Ihnen der Arbeitsspeicher ausgeht und Sie eine schnelle Lösung benötigen – Sie sollten ihn nur verwenden, wenn Sie genau wissen, wo Ihr gesamter Arbeitsspeicher zugewiesen wird und warum er beibehalten werden muss. Doch selbst wenn Sie sicher sind, dass Ihre App den großen Haufen rechtfertigen kann, sollten Sie es so weit wie möglich vermeiden, sie anzufordern. Die Verwendung des zusätzlichen Speichers wird sich zunehmend nachteilig auf die allgemeine Benutzererfahrung auswirken, da die Garbage Collection länger dauert und die Systemleistung beim Wechseln von Aufgaben oder beim Ausführen anderer gängiger Vorgänge möglicherweise langsamer ist.
Nach der Arbeit unerträglich mit out of memory errors
Ich würde sagen, dass es keine Sünde ist, dies dem Manifest hinzuzufügen, um das Oom-Problem zu vermeiden
Überprüfen des App-Verhaltens auf der Android-Laufzeit (ART)
Die Android-Laufzeit (ART) ist die Standardlaufzeit für Geräte mit Android 5.0 (API-Ebene 21) und höher. Diese Laufzeit bietet eine Reihe von Funktionen, die die Leistung und Laufruhe der Android-Plattform und -Apps verbessern. Weitere Informationen zu den neuen Funktionen von ART finden Sie in Wir stellen KUNST vor.
Einige Techniken, die bei Dalvik funktionieren, funktionieren jedoch nicht bei ART. In diesem Dokument erfahren Sie, worauf Sie achten müssen, wenn Sie eine vorhandene App migrieren, damit sie mit ART kompatibel ist. Die meisten Apps sollten nur funktionieren, wenn sie mit ART ausgeführt werden.
Behandlung von Problemen mit der Garbage Collection (GC).
Unter Dalvik finden es Apps häufig nützlich, System.gc() explizit aufzurufen, um die Garbage Collection (GC) anzufordern. Dies sollte bei ART weitaus weniger notwendig sein, insbesondere wenn Sie die Garbage Collection aufrufen, um Vorkommen vom Typ GC_FOR_ALLOC zu verhindern oder die Fragmentierung zu reduzieren. Sie können überprüfen, welche Laufzeit verwendet wird, indem Sie System.getProperty(“java.vm.version”) aufrufen. Wenn ART verwendet wird, ist der Wert der Eigenschaft “2.0.0” oder höher.
Darüber hinaus wird im Android Open-Source Project (AOSP) ein kompaktierender Garbage Collector entwickelt, um die Speicherverwaltung zu verbessern. Aus diesem Grund sollten Sie die Verwendung von Techniken vermeiden, die mit der Komprimierung von GC nicht kompatibel sind (z. B. das Speichern von Zeigern auf Objektinstanzdaten). Dies ist besonders wichtig für Apps, die das Java Native Interface (JNI) verwenden. Weitere Informationen finden Sie unter Verhindern von JNI-Problemen.
Verhindern von JNI-Problemen
Die JNI von ART ist etwas strenger als die von Dalvik. Es ist eine besonders gute Idee, den CheckJNI-Modus zu verwenden, um allgemeine Probleme zu erkennen. Wenn Ihre App C/C++-Code verwendet, sollten Sie den folgenden Artikel lesen:
Sie können auch nativen Speicher verwenden (NDK & JNI), sodass Sie die Heap-Größenbeschränkung tatsächlich umgehen.
Hier sind einige Beiträge dazu entstanden:
-
So cachen Sie Bitmaps im nativen Speicher
-
https://stackoverflow.com/a/9428660/1761003
-
JNI-Bitmap-Operationen , um OOM bei der Verwendung großer Bilder zu vermeiden
und hier ist eine Bibliothek dafür gemacht:
Hast du das gelesen? developer.android.com/training/displaying-bitmaps/…
– 2Dee
8. September 2014 um 7:56 Uhr
Versuchen Sie Folgendes: stackoverflow.com/questions/19558713/…
– Schlampig
8. September 2014 um 8:00 Uhr
Nein, aber wie gesagt, ich habe ein sehr kleines Bild (max. 600 KB). Ich denke, das ist für ein größeres Bild. @2Dee
– Utku Soytaş
8. September 2014 um 8:00 Uhr