大位图内存不足异常

Posted

技术标签:

【中文标题】大位图内存不足异常【英文标题】:outofmemory exception for Large Bitmap 【发布时间】:2016-08-23 01:36:19 【问题描述】:

我找到了很多关于如何加载大型位图和避免内存不足异常的文档。但问题是我必须从我的 MediaStore.Images.media 中获取图像,所以经典 谷歌文档中指出的decodeFile(path,options) 对我不起作用

正如您在下面看到的,我取消了 // Bitmap photo= Mediastore.Images 行的注释,这是触发内存不足的行。在另一边添加 Bitmap bm=BitmapFactory.decodeFile(selectedImageToUri,options) 行返回 null,尽管编译器可以看到 selectedImageToUri 中的路径(表示图片所在的内容提供者)而不是我设置为 8 的选项值,因为我想缩小所有图片

我的问题是如何在 bm 中插入引用用户在图库中选择的图像的位图。在BitMap photo 行中不返回 null 并且工作得非常好,但我取消了注释,因为在我更改了几个图像后给了我内存不足的异常。

@Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable final Bundle savedInstanceState) 


        if (flagVariable) 

            if (selectedImageToUri != null) 


                // BitMap photo = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), Uri.parse(selectedImageToUri));


                final BitmapFactory.Options options= new BitmapFactory.Options();
                options.inSampleSize=8;


                Bitmap bm = BitmapFactory.decodeFile(selectedImageToUri, options);

                pic = new BitmapDrawable(bm);


                getActivity().getWindow().setBackgroundDrawable(pic);


             else 
                getDefaultImageBackground(inflater, container);

            


            hiddenList = inflater.inflate(R.layout.fragment_as_list_layout_temp, container, false);

         else 
            getDefaultImageBackground(inflater, container);
        

        listView = (ListView) hiddenList.findViewById(R.id.list_hidden);

【问题讨论】:

【参考方案1】:

MediaStore.getBitmap只是一个简单的方便的方法,看起来是这样的:

public static final Bitmap getBitmap(ContentResolver cr, Uri url)
                throws FileNotFoundException, IOException 
    InputStream input = cr.openInputStream(url);
    Bitmap bitmap = BitmapFactory.decodeStream(input);
    input.close();
    return bitmap;

您可以基于此创建自己的方法,该方法采用 options 并在 BitmapFactory 上调用不同的重载:

public static final Bitmap getBitmap(ContentResolver cr,
                                     Uri url,
                                     BitmapFactory.Options options)
                throws FileNotFoundException, IOException 
    InputStream input = cr.openInputStream(url);
    Bitmap bitmap = BitmapFactory.decodeStream(input, null, options);
    input.close();
    return bitmap;

用法:

final BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;

Bitmap bm = getBitmap(getActivity().getContentResolver(),
                      Uri.parse(selectedImageToUri),
                      options);

【讨论】:

感谢韦斯顿,感谢您的编辑和回复,所以我给您一个声誉点,但我接受作为回复 Attaulah,这是第一个并且进一步为我提供了一个不错的快速胜利,增加了大堆清单也是一个有趣的选择(虽然当然不是最佳的) 这取决于你,但为了记录,我的答案是第一位的。答案的顺序不是按时间顺序排列的。 我打心底里很抱歉我不认识韦斯顿。交换你的善意试图找到一个你寻求帮助的帖子将是一个乌托邦,因为与你相比,我的知识水平绝对是遥不可及的,我什至仍然不是一个谋生的android开发人员。我对你头像的金头最真诚的赞美,真是太棒了。真希望将来对你有价值。再次对不起韦斯顿 别傻了!这取决于你接受哪一个,不要道歉。【参考方案2】:

我在这个问题上花了很多时间,但没有人会给我确切的答案,最后我解决了。首先创建方法并提供Image URI作为参数,这将返回位图,我在这里计算图像大小的基础,我们可以管理内存以及图像并以位图形式获取精确的图像。

您甚至可以显示 5000×8000 和 12MiB 的图片,没有任何错误代码经过测试,只需复制粘贴到您的班级即可享受。

使用

Bitmap mBitmap = getPhoto(MYIMAGEURI);

为方法提供 URI 并获取位图

Bitmap getPhoto(Uri selectedImage) 
    DisplayMetrics metrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(metrics);
    int height = metrics.heightPixels;
    int width = metrics.widthPixels;
    Bitmap photoBitmap = null;
    InputStream inputStream = null;
    try 
        inputStream = getContentResolver().openInputStream(selectedImage);
     catch (FileNotFoundException e) 
        e.printStackTrace();
    
    BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
    bitmapOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeStream(inputStream, null, bitmapOptions);
    int imageWidth = bitmapOptions.outWidth;
    int imageHeight = bitmapOptions.outHeight;

    @SuppressWarnings("unused")
    InputStream is = null;
    try 
        is = getContentResolver().openInputStream(selectedImage);
     catch (FileNotFoundException e) 
        e.printStackTrace();
    
    float scale = 1.0f;

    if (imageWidth < imageHeight) 
        if (imageHeight > width * 1.0f) 
            scale = width * 1.0f / (imageHeight * 1.0f);
        

     else 
        if (imageWidth > width * 1.0f) 
            scale = width * 1.0f / (imageWidth * 1.0f);
        

    

    photoBitmap = decodeSampledBitmapFromResource(this,
            selectedImage, (int) (imageWidth * scale),
            (int) (imageHeight * scale));
    return photoBitmap;

使用图像大小解码位图样本

public static Bitmap decodeSampledBitmapFromResource(Context context,
            Uri uri, int reqWidth, int reqHeight) 

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    InputStream is = null;
    try 
        is = context.getContentResolver().openInputStream(uri);
     catch (FileNotFoundException e1) 
        // TODO Auto-generated catch block
        e1.printStackTrace();
    
    BitmapFactory.decodeStream(is, null, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth,
            reqHeight);

    // Decode editBitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    InputStream inputs = null;
    try 
        inputs = context.getContentResolver().openInputStream(uri);
     catch (FileNotFoundException e) 
        e.printStackTrace();
    

    return BitmapFactory.decodeStream(inputs, null, options);

计算样本量

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) 

        // Calculate ratios of height and width to requested height and
        // width
        final int heightRatio = Math.round((float) height
                / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will
        // guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = Math.min(heightRatio, widthRatio);
        // inSampleSize = heightRatio < widthRatio ? heightRatio :
        // widthRatio;
    

    return inSampleSize;

或者可以使用manifiest.xml中的一行代码来解决 在应用程序标签中使用这个

android:largeHeap="true"

【讨论】:

不要忘记关闭这些流。看我的回答。 请在需要关闭流的地方编辑我的答案。 不,你的答案还有太多其他问题,如果我只编辑那一点,我会对其他问题感到内疚,我没有时间完全重写。跨度> 我会选择韦斯顿的回复。当我使用三星 S5 时,LargeHeap 根本不起作用。出于安全原因,内部内容提供程序不允许检索 MediaStore 图像,因此我不得不在内部使用 FileOutputStream 保存位图,但它仍然给我带来了很多问题,因为我遇到了异常:画布试图使用回收的位图。 Weston 的回复让我可以快速对图像进行二次采样,并教我如何覆盖方法,也是第一个回复。所以我很抱歉,我是SO的新手。希望你没问题。对不起 我的回答中已经给出了替代方法。当您使用 options.inSampleSize = 8;小图像缩小到 8 倍,这种方法只适用于大图像,如果你想使用小图像,那么图像质量会变得太低。检查您的小图像方法,它将显示图像的结果。现在,如您所愿,您正在使用什么方法。但是当您想使用图像质量时,根据您的设备,我的方法非常适合。

以上是关于大位图内存不足异常的主要内容,如果未能解决你的问题,请参考以下文章

从服务器获取位图时内存不足?

如何检测大对象堆是不是导致内存不足异常

避免位图内存不足错误的建议

Flink on Yarn 提交任务由于内存不足产生的异常调试

如何从 Android 中的大位图加载图块?

保留内存是不是会导致内存不足异常