我得到一个奇怪的Out of Memory错误解码一个可绘制的图像资源960x926px jpg,分配3555856字节。 图像仅放在drawable-xxhdpi(3x)中,我使用的是hdpi(1.5x)设备。 两个问题:
为什么我在堆中有足够的可用内存时会出现错误?
为hdpi设备分配应该是((960/2)x(926/2))x 4 = 888960字节(不是3555856)?
有人可以解释一下吗?
注意:问题是为什么在拥有22.5MB可用内存的情况下获得3.5MB的OOM分配(参见日志)
03-18 17:30:15.050 32750-32750 /? D / dalvikvm:GC_FOR_ALLOC被释放 10809K,49%免费23735K / 46087K,暂停89ms,总计89ms
03-18 17:30:15.050 32750-32750 /? I / dalvikvm-heap:强制收集 SoftReferences用于3555856字节分配
03-18 17:30:15.160 32750-32750 /? D / dalvikvm:GC_BEFORE_OOM释放29K, 49%免费23705K / 46087K ,暂停103毫秒,总计103毫秒
03-18 17:30:15.160 32750-32750 /? E / dalvikvm-heap:内存不足 3555856字节分配。
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:"主要" prio = 5 tid = 1 RUNNABLE
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:|基团="主" SCOUNT = 0 dsCount = 0 obj = 0x418fc6a0 self = 0x4010c008
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:| sysTid = 32750 nice = 1 sched = 0/0 cgrp = apps handle = 1075251280
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:| schedstat =(0 0 0) utm = 3807 stm = 859 core = 0
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:636)
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:484) 03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:512)
03-18 17:30:15.160 32750-32750 /?我/ dalvikvm:at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:542)
答案 0 :(得分:2)
1)如果您在hdpi文件夹中没有较小的版本,它将使用最接近的匹配。因此,如果不存在hdpi或drawable / version,它将使用xxhdpi。
2)它不会自动缩放。它将以完整尺寸读取。
3)如果这会导致OOM,一般情况下你可能会使用太多内存。
答案 1 :(得分:2)
你获得OOM的原因是因为当图像被解码到内存中时,它的位图比图像分辨率需要更多的大小(差不多4次,我不确定这个值)。
使用图片时要记住几点:
因此,请尝试使用以下解决方案:
第1步:查找屏幕尺寸
取自here
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
步骤1:创建异步任务以解码位图
如果您使用异步任务作为内部类,则考虑使用公共静态内部类(以节省内存泄漏问题)并保留要加载图像的imageview的弱引用。还可以将图像资源或文件或流传输到构造函数。在下面的代码中,假设您要解码资源。同时传递步骤1中计算的宽度和高度。
public static class BitmapDecodeTask extends AsyncTask<Void, Void, Bitmap> {
//the reason to use a weak reference is to protect from memory leak issues.
private WeakReference<Context> mContextReference;
private WeakReference<ImageView> mImageViewReference;
private int mResourceId;
private int mRequiredWidth;
private int mRequiredHeight;
public BitmapDecodeTask(Context context, ImageView imageView, int resourceId, int width, int height) {
this.mContextReference = new WeakReference<>(context);
this.mImageViewReference = new WeakReference<>(imageView);
this.mResourceId = resourceId;
this.mRequiredWidth = width;
this.mRequiredHeight = height;
}
@Override
protected Bitmap doInBackground(Void... params) {
Context context = mContextReference.get();
if(context != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), mImageResourceId, options);
//set inSampleSize
options.inSampleSize = calculateInSampleSize(options);
//set inJustDecodeBounds = false;
options.inJustDecodeBounds = false;
//decode
return BitmapFactory.decodeResource(getResources(), mImageResourceId, options);
}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//check if imageview is available or not
ImageView imageView = mImageViewReference.get();
if(imageView != null && bitmap != null) {
imageView.setImageBitmap(bitmap);
}
}
public static int calculateInSampleSize(BitmapFactory.Options options) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > mRequiredHeight || width > mRequiredWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > mRequiredHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
}
参考文献:
答案 2 :(得分:1)
位图的内存大小是宽度x高度x每个颜色的位数。我相信你没有使用任何特定的选项,你使用每像素4个字节(红色1个字节,绿色1个字节,蓝色1个字节,Alpha 1个字节)。所以:960 * 926 * 4 = 3555840字节。
关于你的OOM:对我来说似乎并不常见的是:
强制收集3555856字节分配的SoftReferences
您在哪里存储分配的位图? Android上应避免使用软引用。
答案 3 :(得分:1)
根据Gabe Sechan的建议,我建议使用像makeappicon这样的网站,它允许您自动缩放任何图像太大。它是一个有用的工具,虽然Android应该为您处理这些缩放问题。
Mimmo Grottoli关于ARGB字节是正确的,所以就我所知,没有什么可担心的。
你最有可能得到这个错误的原因是因为创建(而不是破坏)位图所涉及的内存泄漏很大,我从一段时间的steg项目中学到了很多困难。
为了清理此内存,您可以覆盖Activity / Fragment的onDestroy()
方法,或在必要时手动执行。
@Override
public void onDestroy() {
yourbitmap.recycle();
yourbitmap = null;
super.onDestroy();
}
希望他对你有所帮助。
答案 4 :(得分:0)
最好使用Glide或picasso等库来进行所有图像操作(解码,调整大小,下载等):
将路径替换为本地文件夹路径或服务器URL
依赖:
使用Glide加载图片
compile 'com.github.bumptech.glide:glide:3.5.2'