Android BitmapFactory.decodeStream内存不足,尽管使用了减少的样本大小
我读过很多关于解码位图时内存分配问题的相关文章,但即使使用了官方网站提供的代码,我仍然无法找到以下问题的解决方案 这是我的密码:Android BitmapFactory.decodeStream内存不足,尽管使用了减少的样本大小,android,bitmap,out-of-memory,Android,Bitmap,Out Of Memory,我读过很多关于解码位图时内存分配问题的相关文章,但即使使用了官方网站提供的代码,我仍然无法找到以下问题的解决方案 这是我的密码: public static Bitmap decodeSampledBitmapFromResource(InputStream inputStream, int reqWidth, int reqHeight) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] bu
public static Bitmap decodeSampledBitmapFromResource(InputStream inputStream, int reqWidth, int reqHeight) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = inputStream.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
InputStream is1 = new ByteArrayInputStream(baos.toByteArray());
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is1, null, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeStream(is2, null, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
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 = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
bitmap = decodeSampledBitmapFromResource(inputStream, 600, 600);
在我看来,3.2MB足够小,可以分配。我哪里做错了?我怎样才能解决这个问题
编辑
在N-Joy研究了这个解决方案后,它可以正常工作,所需尺寸为300,但我所需的尺寸为800,因此我仍然得到了错误。看一下这个视频。不要按照视频中的建议使用system.gc()。使用MAT分析仪查找内存泄漏。返回的位图太大,可能导致内存泄漏 您可能保留了以前的位图引用。我猜您多次执行此代码,但从未执行过
bitmap.recycle()
。内存将不可避免地耗尽。您似乎有大图像要显示
您可以下载图像并保存到SD卡(),然后您可以使用
从SD卡显示图像的代码。这是用户在玩大型位图时通常会遇到的一个常见问题,尽管用户无法操作精确的解决方案,但在网站、和上讨论了许多问题 我偶然发现了一个库,它可以顺利地管理位图和下面列出的其他链接。希望这有帮助
我在位图内存使用方面遇到了很多问题 结果:
- 大多数设备的图形堆内存有限,大多数小型设备的整体应用程序内存限制为16MB,而不仅仅是您的应用程序
- 如果适用,请使用4位、8位或16位位图
- 尝试从头开始绘制形状,如果可能,请忽略位图
decodeSampledBitmapFromResource
的内存效率不高,因为它使用3个流:ByteArrayOutputStream baos、ByteArrayInputStream为1和ByteArrayInputStream为2,每个流存储图像的相同流数据(每个流一个字节数组)
当我用我的设备(LG nexus 4)将SD卡上的2560x1600图像解码到目标尺寸800时,需要这样做:03-13 15:47:52.557: E/DecodeBitmap(11177): dalvikPss (beginning) = 1780
03-13 15:47:53.157: E/DecodeBitmap(11177): dalvikPss (decoding) = 26393
03-13 15:47:53.548: E/DecodeBitmap(11177): dalvikPss (after all) = 30401 time = 999
我们可以看到:分配了太多的内存(28.5 MB),仅用于解码4096000个像素图像
解决方案:我们读取输入流并将数据直接存储到一个字节数组中,然后使用该字节数组进行其余工作示例代码:
public Bitmap decodeSampledBitmapFromResourceMemOpt(
InputStream inputStream, int reqWidth, int reqHeight) {
byte[] byteArr = new byte[0];
byte[] buffer = new byte[1024];
int len;
int count = 0;
try {
while ((len = inputStream.read(buffer)) > -1) {
if (len != 0) {
if (count + len > byteArr.length) {
byte[] newbuf = new byte[(count + len) * 2];
System.arraycopy(byteArr, 0, newbuf, 0, count);
byteArr = newbuf;
}
System.arraycopy(buffer, 0, byteArr, count, len);
count += len;
}
}
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(byteArr, 0, count, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
options.inPurgeable = true;
options.inInputShareable = true;
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
int[] pids = { android.os.Process.myPid() };
MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (decoding) = " + myMemInfo.dalvikPss);
return BitmapFactory.decodeByteArray(byteArr, 0, count, options);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
进行计算的方法:
public void onButtonClicked(View v) {
int[] pids = { android.os.Process.myPid() };
MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss);
long startTime = System.currentTimeMillis();
FileInputStream inputStream;
String filePath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/test2.png";
File file = new File(filePath);
try {
inputStream = new FileInputStream(file);
// mBitmap = decodeSampledBitmapFromResource(inputStream, 800, 800);
mBitmap = decodeSampledBitmapFromResourceMemOpt(inputStream, 800,
800);
ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setImageBitmap(mBitmap);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (after all) = " + myMemInfo.dalvikPss
+ " time = " + (System.currentTimeMillis() - startTime));
}
结果是:
03-13 16:02:20.373: E/DecodeBitmap(13663): dalvikPss (beginning) = 1823
03-13 16:02:20.923: E/DecodeBitmap(13663): dalvikPss (decoding) = 18414
03-13 16:02:21.294: E/DecodeBitmap(13663): dalvikPss (after all) = 18414 time = 917
解码位图时内存不足问题通常与解码的图像大小无关。 当然,如果您尝试打开一个5000x5000px的映像,您将失败并出现OutOfMemoryError,但是对于800x800px的大小,它是完全合理的,应该可以正常工作 如果您的设备内存不足,且图像容量为3.2MB,则可能是因为您在应用程序中的某个位置泄漏了上下文 这是第一部分: 我想问题不在你们的布局中,问题在你们的其他地方 你的密码。而且可能您正在某处泄漏上下文 这意味着您在不应该使用的组件中使用活动上下文,从而防止它们被垃圾收集。因为这些组件通常由活动持有,所以这些活动不是GC,您的java堆将快速增长,并且您的应用程序有时会崩溃 正如Raghunandan所说,您必须使用MAT来查找哪个活动/组件被保存,并删除上下文泄漏 目前我发现检测上下文泄漏的最好方法是方向改变。 例如,多次旋转ActivityMain,运行MAT并检查是否只有一个ActivityMain实例。如果有多个(与旋转变化一样多),则表示存在上下文泄漏 几年前我发现了一个。也许现在有更好的 关于内存泄漏的其他帖子:
ARGB_8888
使用更多内存,因为它需要Alpha颜色值,所以我的建议是如前所述使用RGB_565
注意:与
ARGB_8888
相比,质量稍低,我之前也遇到过同样的问题。。我使用这个函数来管理它,你可以根据需要的宽度和高度来获取比例
private Bitmap decodeFile(FileInputStream f)
{
try
{
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(f,null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true)
{
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(f, null, o2);
}
catch (FileNotFoundException e) {}
return null;
}
私有位图解码文件(FileInputStream f)
{
尝试
{
//解码图像大小
BitmapFactory.Options o=新的BitmapFactory.Options();
o、 inJustDecodeBounds=true;
解码流(f,null,o);
//找到正确的刻度值。它应该是2的幂。
所需的最终int_尺寸=70;
内部宽度=o.向外宽度,高度=o.向外高度;
int标度=1;
while(true)
{
如果(width_tmp/2即使我删除它也不能解决问题,有没有办法请告诉我我被卡住了很长时间删除也不能解决问题。要解决你的问题,请使用mat分析器找出位图占用了多少内存。我猜它占用了太多空间,这会导致内存泄漏。是的,它占用了太多内存,但如何解决这个问题呢大问题压缩应该减少一些内存使用。但是如果它仍然很大,导致内存泄漏,我恐怕什么也做不了。尝试压缩位图。如何压缩它?它会降低质量吗?每当你使用位图时,你都应该回收它。我无法回答,因为我不知道你在使用什么或如何使用它。是的,我不知道ried了您的解决方案,但仍然不走运:(我还缺少什么吗?假设calculateInSampleSize()
re,这将使用与其当前实现完全相同的内存量
private Bitmap decodeFile(FileInputStream f)
{
try
{
//decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(f,null,o);
//Find the correct scale value. It should be the power of 2.
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true)
{
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
//decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(f, null, o2);
}
catch (FileNotFoundException e) {}
return null;
}