在我的应用程序中,我从JPEG和PNG文件中加载了几张图像。当我将所有这些文件放入assets目录并以这种方式加载时,一切正常:
InputStream stream = getAssets().open(path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
但是当我尝试从SD卡加载完全相同的图像时,我得到一个OutOfMemory异常!
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
return new BitmapDrawable(bitmap);
这是我在日志中得到的:
11-05 00:53:31.003: ERROR/dalvikvm-heap(13183): 827200-byte external allocation too large for this process.
11-05 00:53:31.003: ERROR/GraphicsJNI(13183): VM won't let us allocate 827200 bytes
...
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): Caused by: java.lang.OutOfMemoryError: bitmap size exceeds VM budget
11-05 00:53:31.053: ERROR/AndroidRuntime(13183): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
...
为什么会发生这种情况?
更新:在真实设备上尝试了这两个 - 似乎我无法将超过12MB的位图加载到所谓的“外部存储器”中(这不是SD卡)。
答案 0 :(得分:8)
我尝试了所有提到的方法here&在其他资源,但我得出的结论是,设置ImageView对null的引用将解决问题:
public Bitmap getimage(String path ,ImageView iv)
{
//iv is passed to set it null to remove it from external memory
iv=null;
InputStream stream = new FileInputStream("/mnt/sdcard/mydata/" + path);
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null);
stream.close();
stream=null;
return bitmap;
}
&安培;你完成了!
注意:虽然它可以解决上述问题,但我建议您检查Tom van Zummeren的优化图像加载。
并且还检查SoftReference:在VM将抛出OutOfMemoryError之前,保证指向软可访问对象的所有SoftReferences都被清除。
答案 1 :(得分:5)
BitmapFactory.Options
并将inSampleSize
设置为> 1来降低负载。编辑:此外,请务必检查您的应用是否有内存泄漏。泄漏位图(具有static
位图是一种很好的方法)会很快耗尽你的可用内存。
答案 2 :(得分:4)
你的API使用可能没什么问题,我想我们所能做的就是推断使用AssetManager比从SD卡打开一个随机文件更少的幕后堆分配。
800KB在任何人的书中都是一个严肃的分配...这无疑是针对解压缩的图像像素。鉴于您知道图像的大小,它的深度是多少?如果它是32bpp,那么尝试使用inPreferredConfig
覆盖它。
答案 3 :(得分:3)
这是一个相当普遍的问题,我们所有人在从SD卡加载图像时都会遇到这个问题。
我发现的解决方案是在使用inJustDecodeBounds加载图片时首先使用decodeFileDescriptor。这实际上不会解码图像,但会给出图像大小。现在我可以适当地缩放它(使用选项),以便调整显示区域的图像大小。它需要,因为手机上的低内存可以很容易地被你的500万像素接管。我认为这是最优雅的解决方案。
答案 4 :(得分:2)
这里有两个问题......
答案 5 :(得分:1)
为什么不使用getCacheDir()将图像移动到手机内部存储中的缓存中,或者使用临时目录存储图像,而不是直接从SD卡加载?
请参阅this,this了解外部内存使用情况。此外,this article可能与您有关。
答案 6 :(得分:1)
使用以下代码,您将永远不会收到以下错误:java.lang.OutOfMemoryError:位图大小超过VM预算
BitmapFactory.Options bounds = new BitmapFactory.Options();
bounds.inSampleSize = 4;
myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
picturesView.setImageBitmap(myBitmap);
答案 7 :(得分:1)
The best solution i found and edited according to my need
public static Bitmap getImageBitmap(String path) throws IOException{
// Allocate files and objects outside of timingoops
File file = new File(thumbpath);
RandomAccessFile in = new RandomAccessFile(file, "rws");
final FileChannel channel = in.getChannel();
final int fileSize = (int)channel.size();
final byte[] testBytes = new byte[fileSize];
final ByteBuffer buff = ByteBuffer.allocate(fileSize);
final byte[] buffArray = buff.array();
@SuppressWarnings("unused")
final int buffBase = buff.arrayOffset();
// Read from channel into buffer, and batch read from buffer to byte array;
long time1 = System.currentTimeMillis();
channel.position(0);
channel.read(buff);
buff.flip();
buff.get(testBytes);
long time1 = System.currentTimeMillis();
Bitmap bmp = Bitmap_process(buffArray);
long time2 = System.currentTimeMillis();
System.out.println("Time taken to load: " + (time2 - time1) + "ms");
return bmp;
}
public static Bitmap Bitmap_process(byte[] buffArray){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inDither=false; //Disable Dithering mode
options.inPurgeable=true; //Tell to gc that whether it needs free memory, the Bitmap can be cleared
options.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
options.inTempStorage=new byte[32 * 1024]; //Allocate some temporal memory for decoding
options.inSampleSize=1;
Bitmap imageBitmap = BitmapFactory.decodeByteArray(buffArray, 0, buffArray.length, options);
return imageBitmap;
}
答案 8 :(得分:1)
感谢所有主题,我找到了一个适用于真实设备的解决方案。 这些技巧都是关于使用
BitmapFactory.Options opts=new BitmapFactory.Options();
opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger
但对我来说这还不够。我的原始图片(取自相机应用程序)是3264x2448。我的正确比例为3,因为我想要一个1024x768的普通VGA图像。
但是将inSampleSize设置为3是不够的:仍然是内存不足异常。 所以最后我选择了迭代方法:我从计算出的正确大小开始,然后增加它,直到我停止出现OOM异常。 对我来说,这是4的样本。
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
// o2.inSampleSize = scale;
float trueScale = o.outWidth / 1024;
o2.inPurgeable = true;
o2.inDither = false;
Bitmap b = null;
do {
o2.inSampleSize = (int) trueScale;
Log.d(TAG, "Scale is " + trueScale);
try {
b = BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (OutOfMemoryError e) {
Log.e(TAG,"Error decoding image at sampling "+trueScale+", resampling.."+e);
System.gc();
try {
Thread.sleep(50);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
trueScale += 1;
} while (b==null && trueScale < 10);
return b;
答案 9 :(得分:0)
尝试另一种方式......
Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");
答案 10 :(得分:0)
您不能依靠GC来回收您的位图内存。 在不需要时,您必须清楚地回收位图。
请参阅位图方法:
void recycle() 释放与此位图像素关联的内存,并将位图标记为“死”,这意味着如果调用getPixels()或setPixels(),它将抛出异常,并且不会绘制任何内容。
答案 11 :(得分:0)
我发现开发Android应用程序时最常见的错误之一是“java.lang.OutOfMemoryError:Bitmap Size Exceeds VM Budget”错误。在更改方向后,我在使用大量位图的活动中发现了这个错误:活动被销毁,再次创建,布局从消耗可用于位图的VM内存的XML中“膨胀”。
垃圾收集器未正确释放先前活动布局上的位图,因为它们已经交叉引用了它们的活动。经过多次实验,我发现了一个很好的解决方案。
首先,在XML布局的父视图上设置“id”属性:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/RootView"
>
...
然后,在Activity的onDestroy()方法中,调用unbindDrawables()方法将refence传递给父View,然后执行System.gc()
@Override
protected void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.RootView));
System.gc();
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
这个unbindDrawables()方法以递归方式探索视图树:
答案 12 :(得分:0)
允许inSampleSize调整最终读取图像的大小。 AssetFileDescriptor的getLength()允许获取文件大小。
您可以根据getLength()改变inSampleSize以防止这样的OutOfMemory:
private final int MAX_SIZE = 500000;
public Bitmap readBitmap(Uri selectedImage)
{
Bitmap bm = null;
AssetFileDescriptor fileDescriptor = null;
try
{
fileDescriptor = this.getContentResolver().openAssetFileDescriptor(selectedImage,"r");
long size = fileDescriptor.getLength();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) (size / MAX_SIZE);
bm = BitmapFactory.decodeFileDescriptor(fileDescriptor.getFileDescriptor(), null, options);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
try {
if(fileDescriptor != null) fileDescriptor.close();
} catch (IOException e) {}
}
return bm;
}