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

Bitmap介绍

作者头像
提莫队长
发布2020-06-02 15:30:14
1.3K0
发布2020-06-02 15:30:14
举报
文章被收录于专栏:刘晓杰刘晓杰

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真实占用的内存大小(getByteCount永远小于等于getAllocationByteCount)

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 * 一个像素所占的内存。 接下来我用一张100*100的图片来说明(success_large是从蓝狐上下载下来的,没有经过压缩,100*100的)

代码语言:javascript
复制
        // 不做处理,默认缩放。
        BitmapFactory.Options options = new BitmapFactory.Options();
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.success_large, 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.success_large, 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);

可以看一下log

代码语言:javascript
复制
bitmap:ByteCount = 90000:::bitmap:AllocationByteCount = 90000
width:150:::height:150
inDensity:320:::inTargetDensity:480
===========================================================================
bitmap_setParams:ByteCount = 10000:::bitmap_setParams:AllocationByteCount = 10000
width:50:::height:50
inDensity:320:::inTargetDensity:160

可以看出:

  • 1.不使用Bitmap复用时,getByteCount()与getAllocationByteCount()的值是一致的;
  • 2.很明显, width和height已经是经过nTargetDensity/inDensity处理过了,真正计算的时候要注意.要么用原来的size,也就是100*100来计算,需要用nTargetDensity/inDensity处理.要么用这个,就不用nTargetDensity/inDensity处理
  • 2.默认情况下,华为NXT-AL10手机在xhdpi的文件夹下,inDensity为320,inTargetDensity为480,内存大小为90000=150*150*4。 3.手动设置inDensity与inTargetDensity,使其比例为2,内存大小为10000=50*50*4

(至于第二点为什么不一样网上很容易搜到答案,就是getResources().getDisplayMetrics().density的原因.原图单位是pixel。可是bitmap.getWidth()返回的值会根据dpi的不同而有所调整)

3.Bitmap如何压缩

答案是inSampleSize(具体实现就不贴出来了)

4.Bitmap如何复用

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

(方法一就不说了,很常见.方法二三都是根据BitmapFactory.Options来的,各位可以自己去看看里面的介绍)

代码语言:javascript
复制
//设为true后,会返回一个mutable图片
public boolean inMutable;

//如果设置的话,加载内容的时候会重用之前的bitmap.如果无法使用,就会抛出 java.lang.IllegalArgumentException 
//必须保证decode的图片是mutable,而且返回的也是mutable图片
//在 KITKAT(19) 之前,必须保证是jpeg或者png,还有两个图片的size必须相同,inSampleSize=1,还有 inPreferredConfig 两者必须一样,好在现在19以前的设备很少了
public Bitmap inBitmap;

//decode图片的临时内存,一般建议是16K
public byte[] inTempStorage;

记下来逐个测试

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

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

接下来看一看log

代码语言:javascript
复制
bitmap = android.graphics.Bitmap@d96dbc2
bitmapReuse = android.graphics.Bitmap@d96dbc2
bitmap:AllocationByteCount = 40000
bitmapReuse:ByteCount = 10000:::bitmapReuse:AllocationByteCount = 40000

可以看到,共用同一块内存,复用的bitmap因为inTargetDensity 和 inDensity的原因大约占到了四分之一的内存(全部分配给了它,但是只用了四分之一) 那如果把inTargetDensity = 160改成640呢?这下分配的内存不够了会怎么样呢?试验一下吧

代码语言:javascript
复制
java.lang.IllegalArgumentException: Problem decoding into existing bitmap

直接爆上面的错,不过也很正常,毕竟分配的内存不够

inTempStorage

其实这个策略glide也是这么用的,测试完了会说明一下

代码语言:javascript
复制
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inTempStorage = new byte[16 * 1024 * 1024];
        options.inDensity = 320;
        options.inTargetDensity = 320;
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.success_large, options);

分配了16K处理40000的图片,下面是log

代码语言:javascript
复制
bitmap:ByteCount = 40000:::bitmap:AllocationByteCount = 40000

把inTempStorage设为1看看呢?也是一样的输出,怪了,为啥呢?https://stackoverflow.com/questions/5523552/bitmapfactory-options-intempstorage-field 这个链接里面有段代码,也就是会默认生成一个16K的inTempStorage(这是低版本的,高版本已经没有对应的代码了,可能移到native方法中了).会不会是这个默认值导致的呢?可是相关资料太少了,本人表示不再研究下去了 另外,经本人测试,用这种方法加载14M左右的图片还是会OOM

代码语言:javascript
复制
JNI DETECTED ERROR IN APPLICATION: JNI NewStringUTF called with pending exception java.lang.OutOfMemoryError: Failed to allocate a 487109388 byte allocation with 8376656 free bytes and 352MB until OOM

由此可见,这个方法也不是加载大图的有效方法

5.综上所述

占用内存公式

  • width和height是以pixel为单位的,就是android studio查看图片看到的size。
  • 一个像素所占的内存一般默认是4

因此,加载大图的措施就很明显了

  • 提高inSampleSize(最常见方法)
  • 控制nTargetDensity和inDensity的比值(这数据和手机屏幕有关,基本不推荐)
  • 减少一个像素占用的内存(会影响图片质量,基本不用)

另外,在一些特殊场合,可以使用inBitmap来复用内存。(inTempStorage貌似无效,具体还要看framework的代码)

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

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