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

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类型的容器, 因为这样会导致bitmap对象太多太大时而没有去释放, 最终导致OOM
2). 在Android2.3之前, 一般都用Map>结构容器来缓存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的对比