前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Bitmap详解

Bitmap详解

作者头像
提莫队长
发布2019-02-21 15:16:57
1.4K0
发布2019-02-21 15:16:57
举报
文章被收录于专栏:刘晓杰刘晓杰

1.bitmap占多少内存

getByteCount()方法是在API12加入的,代表存储Bitmap的色素需要的最少内存。API19开始getAllocationByteCount()方法代替了getByteCount()。 这是API26的

代码语言:javascript
复制
    public final int getByteCount() {
        if (mRecycled) {
            Log.w(TAG, "Called getByteCount() on a recycle()'d bitmap! "
                    + "This is undefined behavior!");
            return 0;
        }
        // int result permits bitmaps up to 46,340 x 46,340
        return getRowBytes() * getHeight();
    }

    public final int getAllocationByteCount() {
        if (mRecycled) {
            Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
                    + "This is undefined behavior!");
            return 0;
        }
        return nativeGetAllocationByteCount(mNativePtr);
    }

getByteCount()与getAllocationByteCount()的区别 一般情况下两者是相等的;如果通过复用Bitmap来解码图片,如果被复用的Bitmap的内存比待分配内存的Bitmap大,那么getByteCount()表示新解码图片占用内存的大小(并非实际内存大小,实际大小是复用的那个Bitmap的大小),getAllocationByteCount()表示被复用Bitmap真实占用的内存大小

2.如何计算Bitmap占用的内存

通常情况下认为 bitmap占用的内存 = width * height * 一个像素所占的内存。 下面是API26里面的一个像素所占的内存

代码语言:javascript
复制
public enum Config {
        ALPHA_8     (1),//With this configuration, each pixel requires 1 byte of memory.
        RGB_565     (3),//Each pixel is stored on 2 bytes
        @Deprecated
        ARGB_4444   (4),//Each pixel is stored on 2 bytes
        ARGB_8888   (5),//Each pixel is stored on 4 bytes   如果没有指明,默认这个
        RGBA_F16    (6),//Each pixels is stored on 8 bytes   高色域高动态范围图像
        HARDWARE    (7);//stored only in graphic memory
    }

通常用的是RGB_565 和 ARGB_8888(默认) 其实这个说法不全对,没有说明场景,同时也忽略了一个影响项:Density。

代码语言:javascript
复制
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
            InputStream is, Rect pad, Options opts) {
        validate(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);
    }

因此,加载一张本地资源图片,那么它占用的内存 = width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存。

代码语言:javascript
复制
        // 不做处理,默认缩放。
        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.aaa, options);
        Log.i(TAG, "bitmap:ByteCount = " + bitmap.getByteCount() + ":::bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
        Log.i(TAG, "width:" + bitmap.getWidth() + ":::height:" + bitmap.getHeight());
        Log.i(TAG, "inDensity:" + options.inDensity + ":::inTargetDensity:" + options.inTargetDensity);

        Log.i(TAG,"===========================================================================");

        // 手动设置inDensity与inTargetDensity,影响缩放比例。
        BitmapFactory.Options options_setParams = new BitmapFactory.Options();
        options_setParams.inDensity = 320;
        options_setParams.inTargetDensity = 160;
        Bitmap bitmap_setParams = BitmapFactory.decodeResource(getResources(), R.mipmap.aaa, options_setParams);
        Log.i(TAG, "bitmap_setParams:ByteCount = " + bitmap_setParams.getByteCount() + ":::bitmap_setParams:AllocationByteCount = " + bitmap_setParams.getAllocationByteCount());
        Log.i(TAG, "width:" + bitmap_setParams.getWidth() + ":::height:" + bitmap_setParams.getHeight());
        Log.i(TAG, "inDensity:" + options_setParams.inDensity + ":::inTargetDensity:" + options_setParams.inTargetDensity);


07-15 14:49:06.298 8496-8496/com.company.glidetest I/MainActivity: bitmap:ByteCount = 538800:::bitmap:AllocationByteCount = 538800
07-15 14:49:06.298 8496-8496/com.company.glidetest I/MainActivity: width:449:::height:300
07-15 14:49:06.298 8496-8496/com.company.glidetest I/MainActivity: inDensity:320:::inTargetDensity:320
07-15 14:49:06.298 8496-8496/com.company.glidetest I/MainActivity: ===========================================================================
07-15 14:49:06.318 8496-8496/com.company.glidetest I/MainActivity: bitmap_setParams:ByteCount = 135000:::bitmap_setParams:AllocationByteCount = 135000
07-15 14:49:06.318 8496-8496/com.company.glidetest I/MainActivity: width:225:::height:150
07-15 14:49:06.318 8496-8496/com.company.glidetest I/MainActivity: inDensity:320:::inTargetDensity:160

可以看出: 1.不使用Bitmap复用时,getByteCount()与getAllocationByteCount()的值是一致的; 2.默认情况下,华为手机在xhdpi的文件夹下,inDensity为320,inTargetDensity为320,内存大小为538800;而538800= 449* 300* 4。 3.手动设置inDensity与inTargetDensity,使其比例为2,内存大小为135000,而 449* 300* 0.5 * 0.5 * 4 = 134700。

3.Bitmap如何压缩

inSampleSize 设置inSampleSize之后,Bitmap的宽、高都会缩小inSampleSize倍。例如:一张宽高为2048x1536的图片,设置inSampleSize为4之后,实际加载到内存中的图片宽高是512x384。占有的内存就是0.75M而不是12M,足足节省了15倍。

代码语言:javascript
复制
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        // 设置inJustDecodeBounds属性为true,只获取Bitmap原始宽高,不分配内存;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);// 这里会找到最适合的inTargetDensity和inDensity
        Log.i(TAG, "inTargetDensity = " + options.inTargetDensity);
        Log.i(TAG, "inDensity = " + options.inDensity);
        Log.i(TAG, "inPreferredConfig = " + options.inPreferredConfig);
        // 计算inSampleSize值;
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // 真实加载Bitmap;
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, 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) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            // 宽和高比需要的宽高大的前提下最大的inSampleSize
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
        Log.i(TAG, "inSampleSize = " + inSampleSize);
        return inSampleSize;
    }

测试代码及打印结果

代码语言:javascript
复制
Bitmap bitmap1 = decodeSampledBitmapFromResource(getResources(), R.mipmap.aaa, 100, 100);
Log.i(TAG, "bitmap1 = " + bitmap1.getAllocationByteCount());

07-15 15:06:28.988 23977-23977/com.company.glidetest I/MainActivity: inTargetDensity = 320
07-15 15:06:28.988 23977-23977/com.company.glidetest I/MainActivity: inDensity = 320
07-15 15:06:28.988 23977-23977/com.company.glidetest I/MainActivity: inPreferredConfig = ARGB_8888
07-15 15:06:28.988 23977-23977/com.company.glidetest I/MainActivity: inSampleSize = 2
07-15 15:06:28.998 23977-23977/com.company.glidetest I/MainActivity: bitmap1 = 135000

还是之前的图片(449* 300)正式算的时候应该是把449向上取整数来算的 450/2* 300/2* 1 * 1 * 4 = 135000 (由此可见其实还和inSampleSize有关)

4.Bitmap如何复用

(1)使用LruCache和DiskLruCache做内存和磁盘缓存; (2)使用Bitmap复用,同时针对版本进行兼容。

代码语言:javascript
复制
        BitmapFactory.Options options = new BitmapFactory.Options();
        // 图片复用,这个属性必须设置;
        options.inMutable = true;
        // 手动设置缩放比例,使其取整数,方便计算、观察数据;
        options.inDensity = 320;
        options.inTargetDensity = 320;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.aaa, options);

        // 使用inBitmap属性,这个属性必须设置;
        options.inBitmap = bitmap;
        options.inDensity = 320;
        // 设置缩放宽高为原始宽高一半;
        options.inTargetDensity = 160;
        options.inMutable = true;
        Bitmap bitmapReuse = BitmapFactory.decodeResource(getResources(), R.mipmap.aaa, options);

        // 复用对象的内存地址;
        Log.i(TAG, "bitmap = " + bitmap);
        Log.i(TAG, "bitmapReuse = " + bitmapReuse);
        Log.i(TAG, "bitmap:AllocationByteCount = " + bitmap.getAllocationByteCount());
        Log.i(TAG, "bitmapReuse:ByteCount = " + bitmapReuse.getByteCount() + ":::bitmapReuse:AllocationByteCount = " + bitmapReuse.getAllocationByteCount());

运行结果 07-15 15:24:40.038 8463-8463/com.company.glidetest I/MainActivity: bitmap = android.graphics.Bitmap@20cffbbc 07-15 15:24:40.038 8463-8463/com.company.glidetest I/MainActivity: bitmapReuse = android.graphics.Bitmap@20cffbbc 07-15 15:24:40.038 8463-8463/com.company.glidetest I/MainActivity: bitmap:AllocationByteCount = 538800 07-15 15:24:40.038 8463-8463/com.company.glidetest I/MainActivity: bitmapReuse:ByteCount = 135000:::bitmapReuse:AllocationByteCount = 538800

可以看到,共用同一块内存,复用的bitmap因为inTargetDensity 和 inDensity的原因大约占到了四分之一的内存(全部分配给了它,但是只用了四分之一)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年07月15日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.bitmap占多少内存
  • 2.如何计算Bitmap占用的内存
  • 3.Bitmap如何压缩
  • 4.Bitmap如何复用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档