wake-up-neo.net

Vorschläge zur Vermeidung von Bitmap-Fehlern wegen fehlenden Speichers

Ich arbeite an einer Android-Anwendung. Die Anwendung hat eine Ansicht, die viele Bilder enthält. Ich hatte einen Fehler. Ich werde versuchen, so viele Informationen wie möglich zu geben, in der Hoffnung, dass mir jemand Anregungen geben kann.

Die Anwendung funktionierte bei allen lokalen Tests hervorragend. Ich habe jedoch viele Abstürze von Benutzern erhalten: Java.lang.OutOfMemoryError: bitmap size exceeds VM budget

Dies ist der Stack-Trace

0       Java.lang.OutOfMemoryError: bitmap size exceeds VM budget
1   at  Android.graphics.Bitmap.nativeCreate(Native Method)
2   at  Android.graphics.Bitmap.createBitmap(Bitmap.Java:507)
3   at  Android.graphics.Bitmap.createBitmap(Bitmap.Java:474)
4   at  Android.graphics.Bitmap.createScaledBitmap(Bitmap.Java:379)
5   at  Android.graphics.BitmapFactory.finishDecode(BitmapFactory.Java:498)
6   at  Android.graphics.BitmapFactory.decodeStream(BitmapFactory.Java:473)
7   at  Android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.Java:336)
8   at  Android.graphics.BitmapFactory.decodeResource(BitmapFactory.Java:359)
9   at  Android.graphics.BitmapFactory.decodeResource(BitmapFactory.Java:385)

Mein größtes Problem ist, dass ich das Problem selbst auf alten Geräten nicht lokal reproduzieren konnte.

Ich habe viele Dinge implementiert, um das Problem zu lösen:

  1. Keine Speicherlecks : Ich habe sichergestellt, dass überhaupt keine Speicherlecks vorhanden sind. Ich habe die Ansichten entfernt, wenn ich sie nicht brauche. Ich habe auch alle Bitmaps recycelt und dafür gesorgt, dass der Müllsammler ordnungsgemäß funktioniert. Und ich habe alle notwendigen Schritte in der onDestroy()-Methode implementiert
  2. Bildgröße korrekt skaliert : Bevor ich das Bild bekomme, bekomme ich seine Größe und berechne die inSampleSize.
  3. Heap-Größe : Ich erkenne auch die Max-Heap-Größe, bevor ich das Bild bekomme, und stelle sicher, dass genügend Speicherplatz vorhanden ist. Wenn nicht genug vorhanden ist, skaliere ich das Bild entsprechend neu.

Code zum Berechnen der korrekten inSampleSize

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)
      {
         if(width > height)
         {
            inSampleSize = Math.round((float) height / (float) reqHeight);
         }
         else
         {
            inSampleSize = Math.round((float) width / (float) reqWidth);
         }
      }
      return inSampleSize;
   }

Code zum Abrufen der Bitmap

    // decodes image and scales it to reduce memory consumption
   private static Bitmap decodeFile(File file, int newWidth, int newHeight)
   {// target size
      try
      {

         Bitmap bmp = MediaStore.Images.Media.getBitmap(getContext().getContentResolver(), Uri.fromFile(file));
         if(bmp == null)
         {
            // avoid concurrence
            // Decode image size
            BitmapFactory.Options option = new BitmapFactory.Options();

            // option = getBitmapOutput(file);

            option.inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
            option.inTargetDensity = res.getDisplayMetrics().densityDpi;

            if(newHeight > 0 && newWidth > 0) 
                option.inSampleSize = calculateInSampleSize(option, newWidth, newWidth);

            option.inJustDecodeBounds = false;
            byte[] decodeBuffer = new byte[12 * 1024];
            option.inTempStorage = decodeBuffer;
            option.inPurgeable = true;
            option.inInputShareable = true;
            option.inScaled = true;

            bmp = BitmapFactory.decodeStream(new FileInputStream(file), null, option);
            if(bmp == null)
            {
               return null;
            }

         }
         else
         {
            int inDensity = res.getDisplayMetrics().densityDpi < DisplayMetrics.DENSITY_HIGH ? 120 : 240;
            int inTargetDensity = res.getDisplayMetrics().densityDpi;
            if(inDensity != inTargetDensity)
            {
               int newBmpWidth = (bmp.getWidth() * inTargetDensity) / inDensity;
               int newBmpHeight = (bmp.getHeight() * inTargetDensity) / inDensity;
               bmp = Bitmap.createScaledBitmap(bmp, newBmpWidth, newBmpHeight, true);
            }
         }

         return bmp;
      }
      catch(Exception e)
      {
         Log.e("Error calling Application.decodeFile Method params: " + Arrays.toString(new Object[]{file }), e);
      }
      return null;
   }

Code zum Berechnen der Bildgröße basierend auf der Heap-Größe für ältere Geräte

private void calculateImagesSize()
   {
      // only for Android older than HoneyComb that does not support large heap
      if(Build.VERSION.SDK_INT < Constants.HONEYCOMB)
      {
         long maxHeapSize = Runtime.getRuntime().maxMemory();
         long maxImageHeap = maxHeapSize - 10485760;
         if(Application.getResource().getDisplayMetrics().densityDpi >= DisplayMetrics.DENSITY_XHIGH)
         {
            maxImageHeap -= 12 * 1048576;
         }
         if(maxImageHeap < (30 * 1048576))
         {
            int screenHeight = Math.min(Application.getResource().getDisplayMetrics().heightPixels, Application.getResource()
               .getDisplayMetrics().widthPixels);
            long maxImageSize = maxImageHeap / 100;
            long maxPixels = maxImageSize / 4;
            long maxHeight = (long) Math.sqrt(maxPixels / 1.5);
            if(maxHeight < screenHeight)
            {
               drawableHeight = (int) maxHeight;
               drawableWidth = (int) (drawableHeight * 1.5);
            }
         }
      }
   }

Ich denke, das Problem liegt beim Heap, manchmal erlaubt das Betriebssystem der Anwendung nicht, maxheapsize zu verwenden. Mein größtes Problem ist auch, dass ich das Problem nicht reproduzieren konnte. Wenn ich also einen Fix versuche, muss ich ein wenig warten, um zu sehen, ob Benutzer den Fehler immer noch erhalten.

Was kann ich mehr versuchen, um Probleme mit dem Arbeitsspeicher zu vermeiden? Anregungen wären sehr dankbar. Danke vielmals

35
Youssef

verwenden Sie diese Funktion einfach zum Dekodieren ... Dies ist die perfekte Lösung für Ihren Fehler. Weil ich auch den gleichen Fehler bekomme und diese Lösung erhalten habe.

public static Bitmap decodeFile(File f,int WIDTH,int HIGHT){
     try {
         //Decode image size
         BitmapFactory.Options o = new BitmapFactory.Options();
         o.inJustDecodeBounds = true;
         BitmapFactory.decodeStream(new FileInputStream(f),null,o);

         //The new size we want to scale to
         final int REQUIRED_WIDTH=WIDTH;
         final int REQUIRED_HIGHT=HIGHT;
         //Find the correct scale value. It should be the power of 2.
         int scale=1;
         while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT)
             scale*=2;

         //Decode with inSampleSize
         BitmapFactory.Options o2 = new BitmapFactory.Options();
         o2.inSampleSize=scale;
         return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
     } catch (FileNotFoundException e) {}
     return null;
 }
8
Mehul Ranpara

Durch Verkleinern/Skalieren der Größe des Bildes können Sie die Out of Memory-Ausnahme,

  BitmapFactory.Options options = new BitmapFactory.Options();
  options.inSampleSize = 6; 
  Bitmap receipt = BitmapFactory.decodeFile(photo.toString(),options);  //From File You can customise on your needs. 
5
vinothp

Hallo du musst die Datei entschlüsseln. Versuchen Sie dazu mit der folgenden Methode. 

  public static Bitmap new_decode(File f) {

        // decode image size

        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        o.inDither = false; // Disable Dithering mode

        o.inPurgeable = true; // Tell to gc that whether it needs free memory,
                                // the Bitmap can be cleared

        o.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the future
        try {
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);
        } catch (FileNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        // Find the correct scale value. It should be the power of 2.
        final int REQUIRED_SIZE = 300;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 1.5 < REQUIRED_SIZE && height_tmp / 1.5 < REQUIRED_SIZE)
                break;
            width_tmp /= 1.5;
            height_tmp /= 1.5;
            scale *= 1.5;
        }

        // decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        // o2.inSampleSize=scale;
        o.inDither = false; // Disable Dithering mode

        o.inPurgeable = true; // Tell to gc that whether it needs free memory,
                                // the Bitmap can be cleared

        o.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the future
        // return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        try {

//          return BitmapFactory.decodeStream(new FileInputStream(f), null,
//                  null);
            Bitmap bitmap= BitmapFactory.decodeStream(new FileInputStream(f), null, null);
            System.out.println(" IW " + width_tmp);
            System.out.println("IHH " + height_tmp);           
               int iW = width_tmp;
                int iH = height_tmp;

               return Bitmap.createScaledBitmap(bitmap, iW, iH, true);

        } catch (OutOfMemoryError e) {
            // TODO: handle exception
            e.printStackTrace();
            // clearCache();

            // System.out.println("bitmap creating success");
            System.gc();
            return null;
            // System.runFinalization();
            // Runtime.getRuntime().gc();
            // System.gc();
            // decodeFile(f);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return null;
        }

    }
4
itsrajesh4uguys

eigentlich liegt das problem bei der entwicklung os. In Android wird dies im Gegensatz zu iOS von Google auf der Grundlage der Kameraauflösung entwickelt. Bitmaps beanspruchen viel Speicherplatz, insbesondere für umfangreiche Bilder wie Fotos. Verschiedene Kameras erfassen Bilder mit unterschiedlichen Pixeln (verschiedene Handys haben unterschiedliche Kamerapixelkapazitäten). Hier in Android, basierend auf diesen Pixeln, wird nur das aufgenommene Bild gespeichert. Daher wird ein Bild mit hoher Auflösung offensichtlich nicht von einem Telefon mit niedriger Pixelkapazität hochgeladen. In Android os werden jeder Anwendung maximal 16 MB zugewiesen. Wenn das hochgeladene Image länger dauert, wird Java.lang.OutofMemoryError: Die Bitmap-Größe überschreitet das Budget von VM und die Anwendung stürzt ab. Siehe hierzu http://developer.Android.com/training/displaying-bitmaps/index.html

0
GvSharma

Ich habe eine Zusammenfassung der Vorschläge in einer anderen StackOverFlow-Frage geschrieben: Android: BitmapFactory.decodeStream () hat nicht genügend Speicher mit einer 400-KB-Datei mit 2 MB freiem Heap

0
Paulo Cheque

Wenn Sie OOM vermeiden möchten, können Sie OOM fangen und die Probengröße erhöhen, bis das Bild aufgelöst werden kann:

private Bitmap getBitmapSafely(Resources res, int id, int sampleSize) {
// res = context.getResources(), id = R.drawable.yourimageid
    Bitmap bitmap = null;
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPurgeable = true;
    options.inSampleSize = sampleSize;
    try {
          bitmap = BitmapFactory.decodeResource(res,
                      id, options);
    } catch (OutOfMemoryError oom) {
        Log.w("ImageView", "OOM with sampleSize " + sampleSize, oom);
        System.gc();
        bitmap = getBitmapSafely(res, id, sampleSize + 1);
    }

    return bitmap;
}

Ich hoffe es hilft.

Es ist nicht geeignet, den Fehler abzufangen, nur eine Problemumgehung.

0
Euporie