Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.big);
Bitmap bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.big);
Bitmap bitmap3 = BitmapFactory.decodeResource(getResources(), R.drawable.big);
创建三个 Bitmap,并且用引用引着,防止回收。
截图是 AS 上的内存监控,最大用内存 191 MB (说好的16MB呢)。
未创建图片时 9.7 MB,一张图 96 MB,两张图 175MB。
三张图就出现了 OOM 的情况:
java.lang.OutOfMemoryError: Failed to allocate a 82944012 byte allocation with 16777120 free bytes and 17MB until OOM
记住 82944012 ,等下回到这个数字。
位图是以怎么样形式存在在内存中?
Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
boolean isMutable, boolean requestPremultiplied,
byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
mNativePtr = nativeBitmap;
mFinalizer = new BitmapFinalizer(nativeBitmap);
}
public void recycle() {
if (!mRecycled && mFinalizer.mNativeBitmap != 0) {
if (nativeRecycle(mFinalizer.mNativeBitmap)) {
// return value indicates whether native pixel object was actually recycled.
// false indicates that it is still in use at the native level and these
// objects should not be collected now. They will be collected later when the
// Bitmap itself is collected.
mBuffer = null;
mNinePatchChunk = null;
}
mRecycled = true;
}
}
关键是 natvieBitmap ,它应该指向了保存像素的空间,可能是个 byte 数组,我猜。
位图的大小
/**
* Each pixel is stored on 4 bytes. Each channel (RGB and alpha
* for translucency) is stored with 8 bits of precision (256
* possible values.)
*
* This configuration is very flexible and offers the best
* quality. It should be used whenever possible.
*/
ARGB_8888 (5);
从 KITKAT 版本起,都是用 ARGB 8888,一像素 4 bytes。
82944012
3600 * 5760 * 4 正好是所需大小。
但是这张 big 的实际像素是:
高和宽都翻了 3 倍。
系统屏幕密度 Denstiy
可以参考:
http://blog.csdn.net/zhaoyw2008/article/details/46008513
举个例子
这里有两部手机,大小一样。
一号机,只有上下两个像素。二号机有八个像素。
如果只要展示纯色背景,比如 ColorDrawable,只要内存里保存一个颜色就行了。
但如果要展示一张上红下绿的位图,1号机需要保存两个像素(红,绿),2好号机需要保存八个像素(红,红,红,红,绿,绿,绿,绿)。
但在 drawable 文件夹只准备了 1*2 分辨率的图片,这是系统就会将位图进行适当的缩放,来满足的2号机的展示。
比如,分辨率高宽都乘以 2,颜色复制。
确定缩放比例的源码:
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
if (opts == null) {
opts = new Options();
}
if (opts.inDensity == 0 && value != null) {
final int density = value.density;
if (density == TypedValue.DENSITY_DEFAULT) {
opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
} else if (density != TypedValue.DENSITY_NONE) {
opts.inDensity = density;
}
}
if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}
return decodeStream(is, pad, opts);
}
inTargetDensity 会读取手机的密度。
而 TypeValue value 已经通过 native 方法取得 drawable 所在文件的 denstiy,
比如 mdpi (160)下的图片,在 xxhdpi (480)等级的手机上就会被放大 3 倍,就像这里的 big。
再举个例子,AS 默认的 icon 就为不同密度的手机准备了各自的版本:
前者密度,后者像素:160 48,240 72,320 96,480 144。
只要少了某一张图,其他的位图就可能被拿来缩放。
结尾