Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/183.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 从外部存储器加载位图时发生OutOfMemory异常_Android_Out Of Memory - Fatal编程技术网

Android 从外部存储器加载位图时发生OutOfMemory异常

Android 从外部存储器加载位图时发生OutOfMemory异常,android,out-of-memory,Android,Out Of Memory,在我的应用程序中,我从JPEG和PNG文件中加载了一些图像。当我将所有这些文件放入assets目录并以这种方式加载时,一切正常: InputStream stream = getAssets().open(path); Bitmap bitmap = BitmapFactory.decodeStream(stream, null, null); stream.close(); return new BitmapDrawable(bitmap); 但当我尝试从sd卡加载完全相同的图像时,我得到了

在我的应用程序中,我从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卡)中。

  • 使用位图进行大量操作时,不要调试应用程序,只需运行它即可。调试器将留下内存泄漏
  • 位图非常昂贵。如果可能,通过创建
    BitmapFactory.Options
    并将
    inSampleSize
    设置为>1,在加载时缩小它们的比例

编辑:另外,一定要检查你的应用程序是否存在内存泄漏。泄漏位图(使用
静态
位图是一种很好的方法)会很快耗尽可用内存。

与其直接从SD卡加载位图,不如使用getCacheDir()将图像移动到手机内部存储器中的缓存中,或者使用临时目录将图像存储在内存中


请参阅,关于外部内存使用。此外,可能与您有关。

您的API使用可能没有问题,我想我们所能做的就是推断使用AssetManager涉及的幕后堆分配比从SD卡打开随机文件少


在任何人的书中,800KB都是一个严肃的分配。。。这无疑是用于解压缩图像像素的。假设您知道图像的大小,那么图像的深度是多少?如果是32bpp,则尝试使用覆盖该值。

我尝试了所有提到的方法&在其他资源中,但我得出结论,将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;
    }
&你完了

注意:虽然它可以解决上述问题,但我建议您检查的优化图像加载


还要检查:在VM抛出OutOfMemoryError之前,所有指向软访问对象的软引用都保证被清除。

尝试另一种方法

Bitmap bmpOrignal = BitmapFactory.decodeFile("/sdcard/mydata/" + path");

您不能依赖GC来回收位图内存。 不需要位图时,必须清楚地循环使用位图

请参见位图方法:

无效回收()
释放与此位图像素相关的内存,并将位图标记为“死”,这意味着如果调用getPixels()或setPixels(),它将引发异常,并且不会绘制任何内容。

这是我们所有人在从SD卡加载图像时都会遇到的一个相当常见的问题


我发现的解决方案是在使用加载图像时首先使用。这实际上不会解码图像,但会给出图像大小。现在我可以适当地缩放它(使用选项),以便调整显示区域的图像大小。这是必要的,因为手机内存不足,很容易被5MP图像取代。我认为这是最优雅的解决方案。

这里有两个问题

  • 位图内存不在VM堆中,而是在本机堆中-请参阅
  • 本机堆的垃圾收集比VM堆慢,因此每次执行活动的onPause或onDestroy时,都需要非常积极地执行bitmap.recycle和bitmap=null

我在开发Android应用程序时发现的最常见错误之一是“java.lang.OutOfMemoryError:位图大小超过VM预算”错误。在改变方向后,我经常在使用大量位图的活动中发现这个错误:活动被销毁,再次创建,布局被“膨胀”,因为XML消耗了位图可用的VM内存

上一个活动布局上的位图未被垃圾收集器正确释放,因为它们交叉引用了其活动。经过多次实验,我找到了一个很好的解决这个问题的办法

首先,在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"
     >
     ...

...
然后,在活动的onDestroy()方法上,调用unbindDrawables()方法,将引用传递给父视图,然后执行System.gc()操作

@覆盖
受保护的空onDestroy(){
super.ondestory();
不可绑定的drawables(findviewbyd(R.id.RootView));
gc();
}
私有void未绑定可提取项(视图){
if(view.getBackground()!=null){
view.getBackground().setCallback(null);
}
if(视图组的视图实例){
对于(int i=0;i<((视图组)视图)。getChildCount();i++){
未绑定的Drawables(((视图组)视图).getChildAt(i));
}
((视图组)视图);
}
}
此unbindDrawables()方法递归探索视图树,并:

  • 删除所有后台绘图项上的回调
  • 删除每个视图组上的子对象

  • 使用下面的代码,您将永远不会得到以下错误:java.lang.OutOfMemoryError:位图大小超过VM预算

                  BitmapFactory.Options bounds = new BitmapFactory.Options();
    
                  bounds.inSampleSize = 4;
    
                  myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath(), bounds);
    
                  picturesView.setImageBitmap(myBitmap);
    

    多亏了所有的线程,我找到了一个在真正的设备上适合我的解决方案。 这些技巧都是关于使用

    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;
    
    //使用inSampleSize进行解码
    BitmapFactory.Options o2=新的BitmapFactory.Options();
    //o2.inSampleSize=刻度;
    浮点数trueScale=o.outWidth/1024;
    o2.InPurgable=真;
    o2.inDither=假;
    位图b=null;
    做{
    o2.inSampleSize=(int)trueScale;
    Log.d(标记“刻度为”
    
    BitmapFactory.Options opts=new BitmapFactory.Options();
    opts.inSampleSize=(int)(target_size/bitmap_size); //if original bitmap is bigger
    
    // 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;
    
    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;
    }