如何获取Bitmap在内存中的大小

Android中经常会使用Bitmap,对于开发者来说,可以很方便的调用getByteCount() or getAllocationByteCount()函数,可以获取到大小,但是您思考过几个问题吗?
1)一张100px*100px的图片在内存中会占用多大内存?
2)内存中如何计算的?
3)getAllocationByteCount 与 getByteCount有什么不同吗?

前言

几个概念,先弄清楚
dip : device independent pixels ,设备无关像素,就是我们经常说的dp。
px:像素,正常画图、ps工具里面所说的像素,平常我们常讲的手机是720p、1080p,其实就是像素,1080720代表长有1080px、宽有720px。
dpi :dots per inch , 直接来说就是一英寸多少个点。常见取值 120,160,240。一般称作像素密度,简称密度。正常我们怎么去算呢?很简单,平常我们所说的手机是720p(1080
720),虽然我们不支持长宽是多少,但是一般会说这个手机是5寸手机,这个5寸就是指对角线长度,那就很简单了,通过平方算出对角线上有多少px,然后除以5寸,得出的值,就是dpi。
density : 直接翻译的话貌似叫 密度。常见取值 1.5 , 1.0 。是一个比值,没有单位,准确来说,就是与160的比值。也许有人会问,为什么以160作为标准?(据我所知,一方面是Google第一款代表性的android手机好像是160,另外一方面最主要的原因是,120、160、240,发现如果以160作为标准的话,除数都是可以有限小数)
分辨率: 横纵2个方向的像素点的数量,常见取值 480X800 ,320X480。
屏幕尺寸: 屏幕对角线的长度。

1.占了多大内存?

Android中一张图片(Bitmap)占用的内存主要和以下几个因数有关:图片长度、图片宽度、单位像素占用的字节数、图片的像素密度、屏幕的像素密度。
计算方法:一张图片(Bitmap)占用的内存=(图片长度*targetdensityDpi/densityDpi)*(图片宽度*targetdensityDpi/densityDpi)*单位像素占用的字节数
长度和宽度不用多做解释 ,单位是像素;关键是单位像素占用的字节数如何获得呢?
这里不得不提一下Bitmap创建的API

Bitmap bitmap = BitmapFactory.decodeResource(Resources res,int id,BitmapFactory.Options opts);

单位像素占用的字节数由其参数BitmapFactory.Options的inPreferredConfig变量决定,默认为Bitmap.Config.ARGB_8888,它是个枚举类型,它可以为以下值

命名描述
ALPHA_8此时图片只有alpha值,没有RGB值,一个像素占用1个字节
ARGB_4444这种格式的图片,看起来质量太差,已经不推荐使用。一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites,共16bites,即2个字节
ARGB_8888一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,即4个字节这是一种高质量的图片格式,电脑上普通采用的格式。它也是Android手机上一个BitMap的默认格式。
RGB_565一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites ,Blue(B)值占5个bites,共16bites,即2个字节.对于没有透明和半透明颜色的图片来说,该格式的图片能够达到比较的呈现效果,相对于ARGB_8888来说也能减少一半的内存开销。因此它是一个不错的选择。另外我们通过android.content.res.Resources来取得一个张图片时,它也是以该格式来构建BitMap的.从Android4.0开始,该选项无效。即使设置为该值,系统任然会采用 ARGB_8888来构造图片

所以一张100px*100px的图片在内存中占用的内存数为:

图片格式计算公式占用内存大小
ALPHA_8100*10010000b
ARGB_4444100100220000b
ARGB_8888100100440000b
RGB_565100100220000b

targetdensityDpi是指手机每英寸所有的像素点(比如三星S6,那就是640),densityDpi是图片所处的资源文件夹(比如是xxh,那就是480)
int( 522 * 640 / 480f + 0.5) * int( 686 * 640 / 480f + 0.5) * 4 = 2546432B

640是三星S6每英寸有的像素,480是图片放置的文件夹xxh,0.5是什么?
BitmapFactory.cpp

static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
    //....省略很多代码

    // Update with options supplied by the client.
    if (options != NULL) {
      //....省略很多代码
        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
            const int density = env->GetIntField(options, gOptions_densityFieldID);
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
            if (density != 0 && targetDensity != 0 && density != screenDensity) {
                scale = (float) targetDensity / density;
            }
        }
    }

    //....省略很多代码

    // Scale is necessary due to density differences.
    if (scale != 1.0f) {
        willScale = true;
        scaledWidth = static_cast<int>(scaledWidth * scale + 0.5f);
        scaledHeight = static_cast<int>(scaledHeight * scale + 0.5f);
    }

    //....省略很多代码

    // now create the java bitmap
    return bitmap::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
}

2.getAllocationByteCount 与 getByteCount有什么不同吗?

getAllocationByteCount 获取的是bitmap所占用的内存的大小,getByteCount获取的是bitmap根据上面规则所计算出的大小,正常情况下,这两者是相同的。但是有一种情况是图片复用是会不一样

 //开启图片的内存复用
        options.inMutable=true;

如果一张bitmap加载开启这个option之后,代表内存可以复用,假设这张图片很大,那么下一张图片加载的时候,复用这块内存区域,就会造成getAllocationByteCount 与 getByteCount结果不同,因为前者返回的是bitmap所占用的实际内存的大小、后者返回的是图片的实际计算大小。

3.如何做好Bitmap的优化?

由上面的分析可从以下几点入手去优化Bitmap OOM相关问题:
1)大图小用,要记得抽样inSampleSize
2)小图大用,可以用矩阵Matrix
3)使用9.png代替其他图片
4)Bitmap如果经常同处加载,比如banner位置,可以考虑开启inMutable
5)想知道Bitmap实际宽和高,不必要加载到内存,可以通过inJustDecodeBounds,如果该 值设为true那么将不返回实际的bitmap,也不给其分配内存空间这样就避免内存溢出了。但是允许我们查询图片的信息这其中就包括图片大小信息
6)如果不需要透明度等信息时,使用RGB_565采样率
7)及时recycle

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itbird01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值