it-swarm.com.de

So lösen Sie Java.lang.OutOfMemoryError-Probleme in Android

Ich habe zwar ein sehr kleines Bild in einem gezeichneten Ordner, aber ich bekomme diesen Fehler von Benutzern. Und ich benutze keine Bitmap-Funktion im Code. Zumindest absichtlich :)

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)

Gemäß diesem stackTrace bekomme ich diesen Fehler in dieser Zeile ('tv' ist eine Textansicht):

tv.setBackgroundResource(R.drawable.yanlis);

Worin besteht das Problem? Wenn Sie weitere Informationen zum Code benötigen, kann ich ihn hinzufügen. Vielen Dank!

59
Utku Soytaş

Sie können die Größe des Heapspeichers nicht dynamisch erhöhen, aber Sie können die Verwendung von mehr anfordern, indem Sie verwenden.

Android: largeHeap = "true"

in manifest.xml können Sie in Ihrem Manifest diese Zeilen einfügen, die für einige Situationen verwendet werden.

<application
    Android:allowBackup="true"
    Android:icon="@mipmap/ic_launcher"
    Android:label="@string/app_name"
    Android:largeHeap="true"
    Android:supportsRtl="true"
    Android:theme="@style/AppTheme">

Gibt an, 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 wurden. Dies gilt nur für die erste in einen Prozess geladene Anwendung. Wenn Sie eine freigegebene Benutzer-ID verwenden, um mehreren Anwendungen die Verwendung eines Prozesses zu ermöglichen, müssen alle diese Option konsistent verwenden, da dies zu unvorhersehbaren Ergebnissen führen kann. Die meisten Apps sollten dies nicht benötigen und sich stattdessen darauf konzentrieren, den Gesamtspeicherbedarf zu reduzieren, um die Leistung zu verbessern. Das Aktivieren dieser Option garantiert auch keine feste Erhöhung des verfügbaren Speichers, da einige Geräte durch den insgesamt verfügbaren Speicher eingeschränkt sind.


Verwenden Sie die Methoden getMemoryClass() oder getLargeMemoryClass(), um die verfügbare Speichergröße zur Laufzeit abzufragen.

Wenn das Problem weiterhin besteht, sollte dies auch funktionieren

 BitmapFactory.Options options = new BitmapFactory.Options();
 options.inSampleSize = 8;
 mBitmapInsurance = BitmapFactory.decodeFile(mCurrentPhotoPath,options);

Wenn ein Wert> 1 eingestellt ist, fordert der Decoder das Unterabtasten des Originalbilds auf und gibt ein kleineres Bild zurück, 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.

Kommen wir zu Image Sampling:

Es lohnt sich beispielsweise nicht, ein Bild mit einer Auflösung von 1024 x 768 Pixel in den Speicher zu laden, wenn es schließlich in einer Miniaturansicht mit 128 x 128 Pixel in einem ImageView angezeigt wird.

Um den Decoder anzuweisen, das Bild zu subsampeln und eine kleinere Version in den Speicher zu laden, setzen Sie inSampleSize in Ihrem BitmapFactory.Options - Objekt auf true. Ein Bild mit einer Auflösung von 2100 x 1500 Pixel, das mit einem inSampleSize von 4 decodiert wird, erzeugt beispielsweise eine Bitmap von ungefähr 512 x 384. Das Laden in den Speicher benötigt 0,75 MB anstatt 12 MB für das gesamte Bild (unter der Annahme einer Bitmap-Konfiguration von ARGB_8888). Hier ist eine Methode zum Berechnen eines Stichprobengrößenwerts, der eine Zweierpotenz basierend auf einer Zielbreite und -höhe ist:

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;
}

Hinweis : Ein Zweierpotenzwert wird berechnet, da der Decoder einen Endwert verwendet, indem er auf die nächste Zweierpotenz abrundet, wie dies nach dem inSampleSize Dokumentation.

Um diese Methode zu verwenden, dekodieren Sie zuerst mit inJustDecodeBounds auf true, übergeben Sie die Optionen und dekodieren Sie dann erneut mit dem neuen inSampleSize-Wert und inJustDecodeBounds auf 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);
}

Mit dieser Methode können Sie auf einfache Weise eine Bitmap beliebig großer Größe in ein ImageView laden, das eine Miniaturansicht mit 100 x 100 Pixeln anzeigt, wie im folgenden Beispielcode gezeigt:

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

Sie können auf ähnliche Weise Bitmaps aus anderen Quellen dekodieren, indem Sie die entsprechende BitmapFactory.decode* - Methode nach Bedarf einsetzen.


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: Link


Es ist keine gute IdeeAndroid:largeHeap="true" Hier ist der Auszug aus Google, der es erklärt,

Die Möglichkeit, einen großen Heap anzufordern, ist jedoch nur für eine kleine Menge von Apps vorgesehen, die die Notwendigkeit rechtfertigen, mehr zu verbrauchen RAM (z. B. eine große Fotobearbeitungs-App). Fordern Sie niemals einen großen Heap an Haufen Sie einfach, weil Ihnen der Arbeitsspeicher ausgeht und Sie eine schnelle Lösung benötigen. Verwenden Sie diese Option nur, wenn Sie genau wissen, wo der gesamte Arbeitsspeicher zugewiesen wird und warum er beibehalten werden muss Um den großen Heap zu rechtfertigen, sollten Sie es nach Möglichkeit vermeiden, ihn anzufordern. Die Verwendung des zusätzlichen Speichers wird sich zunehmend nachteilig auf die Gesamterfahrung des Benutzers auswirken, da die Speicherbereinigung länger dauert und die Systemleistung möglicherweise langsamer ist, wenn Aufgaben gewechselt oder andere häufige Aufgaben ausgeführt werden Operationen.

Nach einer exkrutativen Arbeit mit out of memory errors Würde ich sagen, dass es keine Sünde ist, dies dem Manifest hinzuzufügen, um das OOM-Problem zu vermeiden


Überprüfen des App-Verhaltens in der Android Runtime (ART)

Die Android Runtime (ART) ist die Standardlaufzeit für Geräte, auf denen Android 5.0 (API-Stufe 21) und höher) ausgeführt wird. Diese Laufzeit bietet eine Reihe von Funktionen, die sich verbessern Leistung und Benutzerfreundlichkeit der Android Plattform und Apps. Weitere Informationen zu den neuen Funktionen von ART finden Sie unter Introducing ART .

Einige Techniken, die auf Dalvik funktionieren, funktionieren jedoch nicht auf ART. In diesem Dokument erfahren Sie, worauf Sie bei der Migration einer vorhandenen App achten müssen, um mit ART kompatibel zu sein. Die meisten Apps sollten nur mit ART funktionieren


Beheben von Garbage Collection (GC) -Problemen

Unter Dalvik ist es für Apps häufig nützlich, System.gc () explizit aufzurufen, um die Garbage Collection (GC) aufzufordern. Dies sollte bei ART weitaus weniger erforderlich sein, insbesondere wenn Sie die Garbage Collection aufrufen, um Ereignisse 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 Garbage Collector für die Komprimierung entwickelt, um die Speicherverwaltung zu verbessern. Aus diesem Grund sollten Sie Techniken vermeiden, die mit der Komprimierung von GC nicht kompatibel sind (z. B. Dies ist besonders wichtig für Apps, die das Java Native Interface (JNI) verwenden. Weitere Informationen finden Sie unter Verhindern von JNI-Problemen.


JNI-Probleme vermeiden

Das JNI von ART ist etwas strenger als das von Dalvik. Es ist besonders empfehlenswert, den CheckJNI-Modus zu verwenden, um häufige Probleme zu beheben. Wenn Ihre App C/C++ - Code verwendet, sollten Sie den folgenden Artikel lesen:


Sie können auch nativen Speicher ( NDK & JNI ) verwenden, also tatsächlich Umgehen Sie die Beschränkung der Heap-Größe.

Hier sind einige Posts dazu:

und hier ist eine Bibliothek, die dafür gemacht wurde:

130
Maveňツ

Ich sehe nur zwei Möglichkeiten:

  1. Sie haben Speicherlecks in Ihrer Anwendung.
  2. Geräte verfügen nicht über genügend Speicher, wenn Sie Ihre Anwendung ausführen.
3
Cativail

Sie sollten einen LRU-Cache-Manager implementieren, wenn Sie mit Bitmap arbeiten

http://developer.Android.com/reference/Android/util/LruCache.htmlhttp://developer.Android.com/training/displaying-bitmaps/cache-bitmap.htmlWann sollte ich eine Bitmap mit LRUCache recyceln?

OR

Verwenden Sie eine Ebenenbibliothek wie Universal Image Loader: 

https://github.com/nostra13/Android-Universal-Image-Loader

BEARBEITEN: 

Wenn ich jetzt mit Bildern und meistens mit Bitmap arbeite, verwende ich Glide, mit dem Sie ein Glide-Modul und einen LRUCache konfigurieren können

https://github.com/bumptech/glide

3
An-droid

Wenn Sie diesen Fehler Java.lang.OutOfMemoryError erhalten, ist dies das häufigste Problem in Android. Dieser Fehler wird von der Java Virtual Machine (JVM) ausgelöst, wenn ein Objekt aufgrund von Speichermangel nicht zugeordnet werden kann.

Versuchen Sie diesen Android:hardwareAccelerated="false" , Android:largeHeap="true" in Ihrer manifest.xml-Datei unter der folgenden Anwendung:

<application
  Android:name=".MyApplication"
  Android:allowBackup="true"
  Android:icon="@mipmap/ic_launcher"
  Android:label="@string/app_name"
  Android:theme="@style/AppTheme"
  Android:hardwareAccelerated="false"
  Android:largeHeap="true" />
1
Gaurav Lambole

Einige Hinweise zum Umgang mit solchen Fehlern/Ausnahmen für Android-Apps:

  1. Aktivitäten & Anwendung haben Methoden wie:

    • onLowMemory
    • onTrimMemory Behandeln Sie diese Methoden, um die Speicherauslastung zu überwachen.
  2. Das Tag in Manifest kann das Attribut 'largeHeap' auf TRUE setzen, wodurch mehr Heap für die App-Sandbox angefordert wird.

  3. Verwalten von In-Memory-Caching und Datenträger-Caching:

    • Bilder und andere Daten wurden möglicherweise während der Ausführung der App im Speicher zwischengespeichert (lokal in Aktivitäten/Fragment und global). sollte verwaltet oder entfernt werden.
  4. Verwendung von WeakReference, SoftReference der Java-Instanzerstellung, speziell für Dateien.

  5. Wenn so viele Bilder vorhanden sind, verwenden Sie die richtige Bibliotheks-/Datenstruktur, die den Speicher verwalten kann, verwenden Sie das Samling von geladenen Bildern und behandeln Sie das Zwischenspeichern von Festplatten.

  6. Behandeln Sie die OutOfMemory-Ausnahme

  7. Befolgen Sie Best Practices für die Codierung 

    • Durchsickern des Gedächtnisses (Nicht alles mit starkem Bezug halten)
  8. Minimieren Sie den Aktivitätsstapel, z. Anzahl der Aktivitäten im Stapel (Nicht alles im Kontext/in der Aktivität halten)

    • Kontext macht Sinn, diejenigen Daten/Instanzen, die nicht außerhalb des Gültigkeitsbereichs (Aktivität und Fragmente) erforderlich sind, halten sie in einen geeigneten Kontext ein, statt globales Referenzhalten.
  9. Minimieren Sie die Verwendung von Statik, viel mehr Singletons.

  10. Achten Sie auf die grundlegenden Speichergrundlagen des Betriebssystems

    • Probleme mit der Fragmentierung des Speichers
  11. Involk GC.Collect () manuell manchmal, wenn Sie sicher sind, dass keine Zwischenspeicherung im Arbeitsspeicher mehr erforderlich ist.

0