从加载图片OOM说起

        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

从加载图片OOM说起_第1张图片

3600 * 5760 * 4 正好是所需大小。

但是这张 big 的实际像素是:

从加载图片OOM说起_第2张图片

高和宽都翻了 3 倍。


系统屏幕密度 Denstiy

可以参考:

http://blog.csdn.net/zhaoyw2008/article/details/46008513


举个例子

从加载图片OOM说起_第3张图片


这里有两部手机,大小一样。

一号机,只有上下两个像素。二号机有八个像素。

如果只要展示纯色背景,比如 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。

只要少了某一张图,其他的位图就可能被拿来缩放。


结尾

要是 OOM 了就是 Bitmap 使用不当。
不是 BitmapDrawable 的锅,也不是 ImageView 的锅。

 





你可能感兴趣的:(Android,Bitmap)