build议避免位图内存不足错误

我正在研究一个Android应用程序。 该应用程序有一个包含大量图像的视图。 我有一个错误,我会尽量提供尽可能多的信息,希望有人能给我一些build议。

该应用程序在所有的本地testing中工作良好。 但是,我收到了很多来自用户的崩溃: 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) 

我最大的问题是,即使在旧设备上,我也无法在本地重现问题。

我已经实现了很多东西来解决这个问题:

  1. 没有内存泄漏 :我确定没有内存泄漏。 当我不需要他们时,我删除了这些意见。 我也回收了所有的位图,并确保垃圾收集器正常工作。 我在onDestroy()方法中实现了所有必要的步骤
  2. 图像大小正确缩放 :获取图像之前,我得到它的尺寸和计算inSampleSize
  3. 堆大小 :我也检测到图像前,最大堆的大小,并确保有足够的空间。 如果没有足够的重量,我相应的图像。

代码来计算正确的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。 另外我最大的问题是,我无法重现这个问题,所以当我尝试修复时,我必须稍等一下,看看用户是否仍然得到错误。

还有什么我可以尝试避免内存不足的问题? 任何build议将不胜感激。 非常感谢

只是使用此function来解码…这是你的错误的完美解决scheme。因为我也得到相同的错误,我得到了这个解决scheme..

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

嗨,你必须解码文件。 为此尝试使用以下方法。

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

通过减less/缩放图像的大小,你可以摆脱内存不足exception,试试这个

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

我在另一个StackOverFlow问题中写了一个build议摘要: Android:BitmapFactory.decodeStream()内存不足,带有一个400MB的文件,带有2MB空闲堆

其实问题是与开发操作系统。 在Android不像iOS,谷歌人开发基于相机的分辨率。 位图占用大量的内存,尤其是丰富的图像,如照片。不同的相机捕捉不同像素的图像(不同的手机具有不同的相机像素容量)。 这里在基于那个像素的android中,只有被捕获的图像会占用内存。 所以显然高分辨率的图像不会被低像素容量的手机上传。 在Android操作系统分配给每个应用程序最大16MB。 如果上传的图像比这更多,那么java.lang.OutofMemoryError:位图大小超出了VM预算,并且应用程序崩溃。 请参阅http://developer.android.com/training/displaying-bitmaps/index.html

如果你想避免OOM,你可以捕捉OOM并增加sampleSize,直到图像可以被parsing:

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

希望能帮助到你。

捕捉错误并不合适,只是一个解决方法。