Android中图片处理优化

温故而知新,系统整理一下。

1. 图片的三级缓存

来源自网络
来源自网络
1.1 什么是三级缓存?
      一级: 内存中的缓存图片对象(Bitmap), 用Map<url, Bitmap>
      二级: 手机sd卡的files或手机内部的files中缓存图片文件(xxx.jpg/png)
      三级: 服务器端保存图片文件
1.2 如何应用三级缓存?
      例如: 如何根据url根据图片显示?
      1). 根据url从一级缓存(Map<url, Bitmap>)中取图片对象, 如果取到了, 直接显示
                注意: 真实项目中使用LruCache<String, Bitmap>来缓存图片()
      2). 如果没有, 根据url中包含图片名称(文件名), 手机的sd卡或内部找对就图片文件加载成bitmap
                如果找到了, 显示并保存到一级缓存
      3). 如果没有, 显示一个默认的图片, 根据url联网请求获取bitmap
                如果没有, 显示一张代表错误的图片
                如果有:
                     a. 保存到一级缓存
                     b. 保存到二级缓存
                     c. 显示图片

2. 大图片的加载显示(避免OOM问题):

1). 问题: 如果将大图片加载到内存中来显示, 可能会导致内存溢出(OOM)
2). 解决思路: 对图片进行压缩加载(本质上只是读取了图片文件的部分数据)
3). 具体办法:
①. 得到图片的宽高的方式:

    1. BitmapFactory.Options options = new BitmapFactory.Options();   options.inJustDecodeBounds = true;   //设置只读取图片文件的边框,这样就不会加载整个图片文件BitmapFactory.decodeResource(getResources(), R.id.myimage, options);   int imageHeight = options.outHeight;  //图片的高 int imageWidth = options.outWidth;  //图片的宽String imageType = options.outMimeType; 
 
               注意: 为了避免OOM异常,最好在解析每张图片的时候都先检查一下图片的大小,
                       除非你非常信任图片的来源,保证这些图片都不会超出你程序的可用内存      

②. 计算取样比例的方式:

    1. public static int calculateInSampleSize(BitmapFactory.Options options,           int reqWidth, int reqHeight) {       // 源图片的高度和宽度       final int height = options.outHeight;       final int width = options.outWidth;       int inSampleSize = 1;       if (height > reqHeight || width > reqWidth) {           // 计算出实际宽高和目标宽高的比率           final int heightRatio = Math.round((float) height / (float) reqHeight);           final int widthRatio = Math.round((float) width / (float) reqWidth);           // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高           // 一定都会大于等于目标的宽和高。           inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;       }       return inSampleSize;   } 
 
           注意: 如果inSampleSize=3, 表示 宽和高上只读取原来数据的1/3, 这样整体大小压缩为原来的1/9

③. 整体处理:

    1. public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,           int reqWidth, int reqHeight) {       // 第一次解析将inJustDecodeBounds设置为true,只获取图片宽,高大小       final BitmapFactory.Options options = new BitmapFactory.Options();       options.inJustDecodeBounds = true; //不会加载整个图片文件    BitmapFactory.decodeResource(res, resId, options);       // 调用上面定义的方法计算inSampleSize值       options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);       // 使用获取到的inSampleSize值再次解析图片       options.inJustDecodeBounds = false; //会根据inSampleSize加载图片的部分数据到内存      return BitmapFactory.decodeResource(res, resId, options);   } 
 
        调用代码如下:
 
    1. mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100)); //将任意一张图片压缩成100*100的缩略图,并在ImageView上展示

3. 缓存图片对象

1). 在内存中缓存图片对象(Bitmap), 不要直接用Map<String, Bitmap>类型的容器, 因为这样会导致bitmap对象太多太大时而没有去释放, 最终导致OOM
2). 在Android2.3之前, 一般都用Map<String, SoftReference<Bitmap>>结构容器来缓存Bitmap对象, 这样在内存不太充足时, 垃圾回收器会将软引用对象释放.
但从2.3开始, 垃圾回收器可能在正常情况下就回收软引用对象, 这样会降低缓存的效果
3). Android的v4兼容包中提供了LruCache来做缓存容器, 它的基本原理为:
把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除

1.    private LruCache<String, Bitmap> mMemoryCache; //缓存Bitmap的容器    @Override   protected void onCreate(Bundle savedInstanceState) {       // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。       // LruCache通过构造函数传入缓存值,以KB为单位。       int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);       // 使用最大可用内存值的1/8作为缓存的大小。       int cacheSize = maxMemory / 8;       mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {           @Override           protected int sizeOf(String key, Bitmap bitmap) {               // 重写此方法来衡量每张图片的大小,默认返回图片数量。               return bitmap.getByteCount() / 1024;           }       };   }    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {       if (getBitmapFromMemCache(key) == null) {           mMemoryCache.put(key, bitmap);       }   }    public Bitmap getBitmapFromMemCache(String key) {       return mMemoryCache.get(key);   }

4. 主动释放bitmap对象

if(!bmp.isRecycle() ){
bmp.recycle() // 回收图片所占的内存
system.gc() // 提醒系统及时回收
}

5. 设置加载图片的色彩模式:

Android中有四种,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存(默认)
RGB_565:每个像素占用2byte内存

默认的模式显示的图片质量是最高的, 但也是占用内存最大的, 而如果使用ARGB_4444模式, 占用的内存就会减少为1/2, 但显示效果差别不明显
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
Bitmap img = BitmapFactory.decodeFile("/sdcard/1.png", options);

========================================================
Android 三大图片加载框架比较

1.哪三大图片加载框架?
1) Picasso
2) Glide
3) Fresco

2.介绍:

Picasso :和Square的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。

Glide:模仿了Picasso的API,而且在他的基础上加了很多的扩展(比如gif等支持),Glide默认的Bitmap格式是RGB_565,比 Picasso默认的ARGB_8888格式的内存开销要小一半;Picasso缓存的是全尺寸的(只缓存一种),而Glide缓存的是跟ImageView尺寸相同的(即5656和128128是两个缓存) 。

FB的图片加载框架Fresco:最大的优势在于5.0以下(最低2.3)的bitmap加载。在5.0以下系统,Fresco将图片放到一个特别的内存区域(Ashmem区)。当然,在图片不显示的时候,占用的内存会自动被释放。这会使得APP更加流畅,减少因图片内存占用而引发的OOM。为什么说是5.0以下,因为在5.0以后系统默认就是存储在Ashmem区了。

3.总结:
Picasso所能实现的功能,Glide都能做,无非是所需的设置不同。但是Picasso体积比起Glide小太多如果项目中网络请求本身用的就是okhttp或者retrofit(本质还是okhttp),那么建议用Picasso,体积会小很多(Square全家桶的干活)。Glide的好处是大型的图片流,比如gif、Video,如果你们是做美拍、爱拍这种视频类应用,建议使用。
Fresco在5.0以下的内存优化非常好,代价就是体积也非常的大,按体积算Fresco>Glide>Picasso
不过在使用起来也有些不便(小建议:他只能用内置的一个ImageView来实现这些功能,用起来比较麻烦,我们通常是根据Fresco自己改改,直接使用他的Bitmap层)

------相关资料推荐
android 图片自定义三级缓存实现以及原理、图片错位解决
android中图片的三级cache策略(内存、文件、网络)
Android高效加载大图、多图解决方案,有效避免程序OOM
Android 编程下图片的内存优化
Android图片内存优化的几点心得
图片优化实例: Android照片墙应用实现,再多的图片也不怕崩溃
Android 三大图片加载框架比较
Android的Glide库加载图片的用法及其与Picasso的对比

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

推荐阅读更多精彩内容