大位图内存不足异常
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 倍,这种方法只适用于大图像,如果你想使用小图像,那么图像质量会变得太低。检查您的小图像方法,它将显示图像的结果。现在,如您所愿,您正在使用什么方法。但是当您想使用图像质量时,根据您的设备,我的方法非常适合。以上是关于大位图内存不足异常的主要内容,如果未能解决你的问题,请参考以下文章