Android Bitmap的大小计算、有效加载以及回收

Bitmap内存占用大小的计算

    一个BitMap位图占用的内存=图片长度*图片宽度*单位像素占用的字节数。使用BitmapFactory来decode一张bitmap时,其单位像素占用的字节数由其参数BitmapFactory.Options的inPreferredConfig变量决定。(注:drawable目录下有的png图使用Bitmap.Config.RGB_565和ARGB_8888decode出来的大小一样,未解)
  • ALPHA_8:只有alpha值,没有RGB值,占一个字节。计算:size=w*h
  • ARGB_4444:一个像素占用2个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites,共16bites,这种格式的图片,看起来质量太差,已经不推荐使用。计算:size=wh2
  • ARGB_8888:一个像素占用4个字节,alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,即4个字节。这是一种高质量的图片格式,电脑上普通采用的格式。android2.3开始的默认格式。计算:size=wh4
  • RGB_565:一个像素占用2个字节,没有alpha(A)值,即不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites ,Blue(B)值占5个bites,共16bites,即2个字节。对于没有透明和半透明颜色的图片并且不需要颜色鲜艳的来说,该格式的图片能够达到比较的呈现效果,相对于ARGB_8888来说也能减少一半的内存开销,因此它是一个不错的选择。计算:size=wh2

实际开发中通过代码获取bitmap大小

     ` 
     @TargetApi(Build.VERSION_CODES.KITKAT)
     public static int getBitmapSize(BitmapDrawable value) {
         Bitmap bitmap = value.getBitmap();
    if (VersionUtils.hasKitKat()) {
        return bitmap.getAllocationByteCount();
    }   if (VersionUtils.hasHoneycombMR1()) {
        return bitmap.getByteCount();
    }
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
    `

大图片的有效加载

    手机拍出来的图片分辨率通常比手机屏幕的分辨率高的多,对于Galaxy Nexus手机拍的2592x1936图片,采用ARGB_8888为2592x1936x4=19M,可能会超出在某些设备上每个应用的内存限制(16m)引发OOM。

针对超出显示区域(通常是imageview或其他view)的大图片的加载,通常做法如下:

    `
    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {   
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
    }
    `

上面代码先通过设置Options.inJustDecodeBounds属性为true,然后执行BitmapFactory.decodeXXX()方法,这一步不会真正加载图片到内存,仅仅是得到图片尺寸信息,存在Options.outHeight和outWidth和outMimeType中,这样就得到了图片在宽和高,再根据目标显示区域的大小计算出缩放比例,并赋值给Options.inSampleSize。如inSampleSize == 4,返回一个原来宽高的1/4的图片,是原来像素数的1/16。比如我们有一张20481536像素的图片,将inSampleSize的值设置为4,就可以把这张图片压缩成512384像素。原本加载这张图片需要占用13M的内存,压缩后就只需要占用0.75M了(假设图片是ARGB_8888类型)。

计算inSampleSize的方法如下:

 `  
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) {
        // Calculate ratios of height and width to requested height and width,计算出实际宽高和目标宽高的比率
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高一定都会大于等于目标的宽和高。
        final int widthRatio = Math.round((float) width / (float) reqWidth);
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        // Anything more than 2x the requested pixels we'll sample down
        final float totalPixels = width * height;
        // further
        final float totalReqPixelsCap = reqWidth * reqHeight * 2;
        while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
            inSampleSize++;
        }
    }   return inSampleSize;
}
 `

bitmap的回收

  • Android3.0之前,Bitmap的像素级数据是存储在native内存上,而Bitmap对象本身是存储在Java虚拟机堆中。在native内存中的像素数据的释放是不可预知的,native内存的增加也会算在堆上,从而容易使用程序崩溃。对于一张确定不再使用的bitmap,要调用recycle()方法并把对象设置为null值。所以对于Android3.0之前的手机,通过DDMS观看Heap信息的时候不显示native部分分配的内存大小,如图所示,加载了一张7M多的图片,但是显示分配Allocated才2M多。但是native分配的内存大小是算在heap上的,所以当heap大小显示的不是HeapMaxSize的时候,也有可能OOM。

  • Android3.0之后,Bitmap像素数据和它对象本身都存储在Java虚拟机的堆内存中,受GC管理的内存,可以通过GC回收。因此调用recycle()并不会加速bitmap的像素级内存的回收。

  • 为了更有效的利用内存,Android3.0起引入BitmapFactory.Options.inBitmap,如果设置了该属性,那么当使用了带有该 Options 参数的 decode 方法在加载内容时,decode 方法会尝试重用一个已经存在的位图。这就意味着位图内存已经被重用了,从而性能得到了改善,并且移除了内存的分配和解除分配。在Android4.4之前,可重用bitmap的条件是宽,高相等,并且计算出来的inSampleSize为1。到了Android4.4,可重用的条件变为新bitmap的大小应该小于或等于被重用bitmap的getAllocationByteCount值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 2021期待与你一起共事,点击查看岗位[https://www.jianshu.com/p/6f4d67fa406...
    闲庭阅读 16,601评论 0 75
  • 一直以来Bitmap都是开发中很棘手的问题,这个问题就是传说中的OOM(java.lang.OutofMemory...
    M悇芐冋忆阅读 4,679评论 0 11
  • 非原创,只是整理,如果里面发现引用的内容没有标识出来,欢迎指出。 一、基本知识 (1)两种图片: 1)矢量图: 矢...
    风再起时ME阅读 2,435评论 0 19
  • 一、Bitmap 内存回收 从3.0开始,Bitmap 像素数据和 Bitmap 对象一起存放在 Dalvik 堆...
    秀花123阅读 1,819评论 1 7
  • “不想工作!”这是个在我脑海里盘踞多年的想法,但是我一直没有勇气实施。因为自己就能把自己否定掉。 否定的理由很多,...
    CICI阅读 424评论 0 3