避免位图内存不足错误的建议 [英] Suggestions to avoid bitmap Out of Memory error
问题描述
我正在开发一个安卓应用程序.该应用程序有一个包含大量图像的视图.我有一个错误,我会尽量提供尽可能多的信息,希望有人能给我一些建议.
I am working on an android application. The application has a view containing lots of image. I had an error, I will try to give as much information as possible hoping someone can give me some suggestions.
该应用程序在所有本地测试中都运行良好.但是,我收到了很多来自用户的崩溃:java.lang.OutOfMemoryError:位图大小超出 VM 预算
The application was working great on all the local testings. However, I received lots of crashes from users: 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)
我最大的问题是即使在旧设备上我也无法在本地重现该问题.
My biggest problem is that I was not able to reproduce the issue locally even on old devices.
我已经实施了很多方法来尝试解决这个问题:
I have implemented lots of things to try to resolve this:
- 无内存泄漏:我确保根本没有内存泄漏.当我不需要它们时,我删除了它们.我还回收了所有位图并确保垃圾收集器正常工作.我在
onDestroy()
方法 中实现了所有必要的步骤 - 图像大小正确缩放:在获取图像之前,我先获取其尺寸并计算
inSampleSize
. - 堆大小:我还在获取图像之前检测最大堆大小,并确保有足够的空间.如果不够,我会相应地重新缩放图像.
- No memory leaks: I made sure there is no memory leaks at all. I removed the views when I dont need them. I also recycled all the bitmaps and made sure the garbage collector is working as it should. And I implemented all the necessary steps in the
onDestroy()
method - Image size scaled correctly: Before getting the image I get its dimension and calculate the
inSampleSize
. - Heap size: I also detect the Max Heap size before getting the image and make sure there is enough space. If there is not enough I rescale the image accordingly.
计算正确 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);
}
}
}
}
我认为问题出在堆上,也许有时操作系统不允许应用程序使用 maxheapsize.另外,我最大的问题是我无法重现该问题,因此当我尝试修复时,我必须稍等一下,看看用户是否仍然收到错误消息.
I think the problem is with the Heap, maybe sometimes the os doesn't allow the application to use the maxheapsize. Also my biggest problem is that I was not able to reproduce the issue, so when I try a fix I have to wait a little to see if users are still getting the error.
我还能尝试避免内存不足问题吗?任何建议将不胜感激.非常感谢
What more could I try to avoid Out of memory issues? Any suggestions would be greatly appreciated. Thanks a lot
推荐答案
只需使用此功能解码...这是您错误的完美解决方案..因为我也遇到了同样的错误并且我得到了这个解决方案..>
just use this function to decode...this is perfect solution for your error..because i also getting same error and i got this solution..
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;
}
这篇关于避免位图内存不足错误的建议的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!