Android Bitmap高效加载

由于Android对单个应用所施加的内存限制,比如16MB,这导致加载Bitmap的时候很容易出现内存溢出,本文主要包含2个方面的内容分析Bitmap内存Bitmap高效加载

一、占用内存

获取bitmap的内存,android提供的方法bitmap.getByteCount()

假如现在mipmap-xhdpi 目录下,有一个 200 * 200 像素的图片,运行加载它,看它输出的尺寸。

Bitmap bitmap= BitmapFactory.decodeResource(getResources(),R.mipmap.btn_go);
bitmap.getByteCount()的输出结果为360000

现在把图片转移到mipmap-xxhdpi 中
bitmap.getByteCount的输出结果为160000

那么mipmap-xhdpi 目录下为何会大这么多呢?

density 影响 Bitmap 内存
放在不同的 mipmap 目录下,对应的不同 density 的设备。density 是设备的固有参数,伴随着 density 的,还有 densityDpi,它也是与设备相关的,表示屏幕每英寸对应多少个点(非像素点)

density.png

上面是官方提供的一张比较经典的图,可以看到,不同的目录,代表不同的 density ,例如 xhdpi 代表的 density 就是 2。而这里的 density 对 densityDip 的基准是 160 ,也就是说,mdpi 对应的 densityDpi 是 160 ,xhdpi 对应的 densityDpi 是 320,同样xxhdpi对应的densityDpi是480

density 和 densityDpi 在 Android 中,都有标准的 API 可以拿到,如下。

DisplayMetrics displayMetrics=new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
Log.i(TAG, displayMetrics.densityDpi+"");
Log.i(TAG,displayMetrics.density+"");

这是我手机的输出结果
densityDpi:480
density:3.0

从getByteCount()源码得知,Bitmap.getByteCount() 是由:
(bitmapWidth * scale) * (bitmapHeight * scale) *(每像素所占字节大小)得出来的
其中:int scale = phoneDensity / inDensity
phoneDensity是我们手机的densityDpi,上述得出我手机是480
inDensity是你图片存放的路径,比如xhdpi是320,xxhdpi是480
BitmapFactory默认色彩度为 ARGB_8888 图片,每像素会占用 4 bytes

在xhdpi下:200 * (480/320) * 200(480/320)4= 360000
在xxhdpi下:200 * (480/480) * 200(480/480)4= 160000

以上就是bitmap所占内存分析。

二、高效加载

1.修改bitmap.config
2.修改inSampleSize

bitmap.config简介

上面提到BitmapFactory默认色彩度为 ARGB_8888
Bitmap.Config一共有四个参数如下:
(这些参数决定了Bitmap位图的配置,会影响到bitmap的像素如何、色彩、以及是否有透明度的能力)

Bitmap.Config ALPHA_8
这个参数每个像素占用1字节的空间。
它代表每个像素点被存储为单个透明度的通道,这对于设置遮罩的图片用例十分有用,它不存储颜色信息。

Bitmap.Config RGB_565
这个参数每个像素占用2字节的空间。
它代表只有RGB通道的编码,其中红色占用5位地址,绿色占用6位地址,蓝色占用5位地址。没有透明度的通道。
使用不透明的位图时,不要求高的色彩保真度使用此配置是不错的选择。

Bitmap.Config ARGB_4444
这个参数每个像素占用2字节的空间。
它一共有四个通道,顾名思义,分别是透明度、红、绿、蓝。每个通道分别占用四位地址,所以一共2字节。
当应用需要节省内存(对色彩质量要求低),同时又需要存储透明度信息,这个配置可以作为选择,但官方比较推荐用ARGB_8888的设置,因为这个的色彩质量差。

Bitmap.Config ARGB_8888
这个参数每个像素占用4字节的空间。
这也是一共4个通道,但不一样的是每个通道站8位地址,因而色彩质量比上一个设置高了特别特别多(16倍)。
能够满足最好的位图质量,在内存充足的情况下,十分推荐使用这个。

inSampleSize简介

通过BitmapFactory.Options来缩放图片,主要是用inSampleSize参数,当inSampleSize=1时,采样后的图片为图片的原始大小,当inSampleSize=2时,采样后的图片宽,高均为原图大小的1/2,而像素数为原图的1/4,假定图片原有的内存是4MB,如果把它的inSampleSize设为2,它的内存就会变成1MB

具体实现代码如下
 public static Bitmap decodeBitmapFromResource(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=1;
        options.inSampleSize=setInSampleSize(options,reqWidth,reqHeight);
        options.inPreferredConfig= Bitmap.Config.ARGB_4444;
        options.inJustDecodeBounds=false;
        return BitmapFactory.decodeResource(res,resId,options);
    }

    public static int setInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
        final  int height=options.outHeight;
        final int width=options.outWidth;
        int inSampleSize=1;
        if(height>reqHeight||width>reqHeight){
            final int halfHeght=height/2;
            final int halfWidht=width/2;
            while ((halfHeght/inSampleSize>=reqHeight)&&(halfWidht/inSampleSize>=reqWidth)){
                inSampleSize *=2;
            }
        }
        return inSampleSize;
    }

有了上面两个方法,实际使用就简单了,比如imageView所期望的图片大小为100X100,这个时候我们就可以这样调用,还是之前存放在xxhdpi中的图片,上述代码中已经把bitmap.config设置成ARGB_4444 ,现在把原先尺寸200X200改成100X100,看下内存是多少

Bitmap bitmap=
decodeBitmapFromResource(getResources(),R.mipmap.btn_go,100,100);
Log.i(TAG,bitmap.getByteCount()+"");

bitmap.getByteCount的输出结果为20000,比之前160000减少了8倍, ARGB_4444较ARGB_8888减少了2倍,设置尺寸100,100传入setInSampleSize()方法中得到inSampleSize=2, 像素数为原图的1/4,内存大小总共就变成了之前的1/8,这样高效加载图片,就会远离oom。

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