Подтвердить что ты не робот

Предложения по предотвращению битовой карты Ошибка в памяти

Я работаю над приложением для Android. Приложение имеет представление, содержащее множество изображений. У меня была ошибка, я постараюсь дать как можно больше информации, надеясь, что кто-то может дать мне несколько советов.

Приложение отлично работало во всех локальных тестах. Однако я получил множество сбоев от пользователей: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

Это трассировка стека

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)

Моя самая большая проблема заключается в том, что я не смог воспроизвести проблему локально даже на старых устройствах.

Я попытался решить множество проблем:

  • Нет утечек памяти. Я убедился, что утечек памяти вообще нет. Я удалил представления, когда мне они не нужны. Я также переработал все растровые изображения и убедился, что сборщик мусора работает так, как должен. И я выполнил все необходимые шаги в методе onDestroy()
  • Правильный размер изображения: перед получением изображения я получаю его измерение и вычисляю inSampleSize.
  • Размер кучи. Я также определяю размер Max Heap перед тем, как получить изображение и убедиться, что достаточно места. Если этого недостаточно, я масштабирую изображение соответствующим образом.

Код для расчета правильного параметра 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;
   }

Код для получения растрового изображения

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

Код для расчета размера изображения на основе размера кучи для более старых устройств

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

Я думаю, что проблема связана с кучей, может быть, иногда os не позволяет приложению использовать maxheapsize. Кроме того, моя самая большая проблема заключается в том, что я не смог воспроизвести проблему, поэтому, когда я пытаюсь исправить, мне нужно немного подождать, чтобы увидеть, все ли пользователи получают ошибку.

Чем больше я могу попытаться избежать проблем с памятью? Любые предложения будут ценны. Большое спасибо

4b9b3361

Ответ 1

просто используйте эту функцию для декодирования... это идеальное решение для вашей ошибки.. потому что я тоже получаю такую ​​же ошибку, и я получил это решение.

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

Ответ 2

Уменьшая/уменьшая размер изображения, вы можете избавиться от исключения из памяти, Попробуйте это

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

Ответ 3

Привет, вам нужно декодировать файл. для этого попробуйте следующий метод.

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

    }

Ответ 5

на самом деле проблема с разработкой os. В андроиде, в отличие от iOS, люди Google разрабатывают это на основе разрешения камеры. Растровые изображения занимают много памяти, особенно для богатых изображений, таких как фотографии. Различные камеры захватывают изображения с разными пикселями (разные мобильные телефоны имеют разную емкость пикселов камеры). Здесь, в андроиде на основе этих пикселей, только захваченное изображение займет память. поэтому изображение с высоким разрешением не будет загружено телефоном с низкой пропускной способностью. В android os выделяет максимум 16 МБ каждому приложению. Если загруженное изображение занимает больше, чем это, тогда java.lang.OutofMemoryError: размер растрового изображения превышает бюджет VM, и приложение аварийно завершает работу. см. это http://developer.android.com/training/displaying-bitmaps/index.html

Ответ 6

Если вы хотите избежать OOM, вы можете поймать OOM и увеличить sampleSize до тех пор, пока изображение не будет разрешено:

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

Надеюсь, что это поможет.

Невозможно поймать ошибку, просто обходной путь.