我的图片四级缓存框架

开发App一定涉及到图片加载、图片处理,那就必须会用到三方的图片框架,要么选择自己封装。至于主流的三方图片框架,就不得不说老牌的ImageLoader、如今更流行的Glide、Picasso和Fresco。但三方的框架本文不会过多介绍。

Glide等框架,毕竟是大神及团队花费很大精力开发和维护的开源框架,他们的设计思路、性能优化、代码规范等等很值得我们学习,之前一段时间也研究过Glide的源码(不得不由衷佩服)。

今天,将自己对于图片加载的思路想法,也借鉴了开源框架的一些好的点,封装了一个图片加载框架——JsLoader。(github地址:https://github.com/shuaijia/JsImageLoader)与大家分享。

文章目录:

前言

至于图片的网络请求,我这里还是使用Android原生提供的HttpUrlConnection;请求网络图片时,开启子线程进行操作,使用线程池对线程进行统一管理;线程间通信还是用了Handler;提到图片加载,大家肯定会立刻想到图片的三级缓存(内存—外存—网络),但我这里提供一个新的思路——四级缓存,与三级缓存不同的是内存又分为了两级,这些稍后会详细介绍到。

本文目的在于和大家分享一个图片框架的封装思路,至于代码的优化,如使用OkHttp替换HttpUrlConnection,使用RxJava替换Handler等,或者有别的不足的地方,也希望大家能够反馈给我,我们一起进步。

先看下整体流程图:

线程池

当前类是一个线程池的管理类。由于当前的线程池,在整个项目中不需要创建多个对象,直接使用单例模式进行创建。

补充:Android中的线程池

在Android中使用线程池的类是:ThreadPoolExecutor;

参数:

int corePoolSize : 线程池中的核心线程数

int maxinumPoolSize :线程池中允许的最大线程数目

long keepAliveTime :非核心线程的超时时间,超出这个时间非核心线程会被回收

TimeUnit unit :非核心线程的超时时间的时间单位

BlockingDeque

workQueue : 保存需要线程池执行的任务的列表

ThreadFactory threadFactory : 线程工厂,只是一个接口,只有一个方法Thread newThread(Runnable r)

在上文展示的类中,我们获取了手机的CPU核心数num,本线程池的核心线程数为CPU数的2倍,最大线程数为CPU核心数的4倍。

内存一级缓存

定义的内存中的一级缓存,即保存作为强引用的位置的HashMap。

此处HashMap使用的是LinkedHashMap。LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的。也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。

正是由于LinkedHashMap具有记忆功能,最近插入的最新访问,就符合了我们的最近最多使用的原则。但由于其遍历速度慢,我们对其容量进行设定,最多30和元素。

重写removeEldestEntry方法,当map的size大于30时,把最近不常用的key放到mSoftBitmapCache中(也就是内存第二级缓存),从而保证mHardBitmapCache的效率。

这里我们在Map中是以Url和Bitmap为Key-Value存储的,由于LinkedHashMap存放少,而且插入移出快,所以这里用的是Bitmap的强引用。

如果LinkedHashMap中包含我们需要的图片,则将图片直接返回。但是注意:此时我们认为此图使用频率更高,因此我们需要先将该元素移出,在加入(这是由于该map后插入的遍历时先读取)。

此为内存的一级缓存。

内存二级缓存

如果内存的LinkedHashMap中未获取到我们想要的图片的话,在二级缓存中进行查找。

这时就用到了ConcurrentHashMap,它的最大特点就是线程安全、高并发、存储量大。由于存储量大,所以我们存放Bitmap时就需要使用其软引用了。

如果此map中含有需要的图片,则先取出其软引用,在从软引用中获取Bitmap对象返回。再将其移至一级缓存中。

内存的读取整体代码如下:

特别声明:在存放入内存前,会将图片进行压缩。

SD卡缓存

内存中没有图片的话,就去文件中查找:

上方代码是根据图片url获取到图片在文件中的路径。

所以的缓存图片,会保存在本包名文件夹下,以url的md5值为名字的文件中,判断到有此文件的话,将文件路径返回。

判断到文件中有我们需要的图片,会拿到文件路径。但是,我们有设定文件有效时间,超过该时间则视为超时,返回null,否则读取该文件。根据图片的路径和当前手机的默认屏幕分辨率进行图片压缩再返回。

文件中有该图片,那就将该图片移植内存中,以提高优先级,而且内存两级中都放入该图片。

网络获取

以上都没拿到图片的话,那只能从网络来获取啦!

对http还是https进行判断,分别对应使用HttpUrlConnection和HttpsUrlConnection。他们代码类似,就只贴其中一个了。

返回码200,表示请求成功,就将输入流返回,否则返回null。

获取输入流后,使用上方代码获取Bitmap对象,原因大家懂的。

获取到图片后,再依次存入sd卡和内存中,因为是好是操作,就在子线程中进行了。

图片压缩

这里主要想介绍下图片的压缩:因为图片加载很容易造成OOM,所以图片压缩处理显得尤为重要。

提供集中压缩方式:

根据期望大小压缩

根据期望尺寸压缩

根据当前手机的默认屏幕分辨率进行图片的压缩

这里就不再贴代码了,可以去我的github中查看。https://github.com/shuaijia/JsImageLoader/blob/master/jsimageloader/src/main/java/com/jia/jsloader/utils/ImageUtil.java

使用

1、添依赖

2、添权限

3、继承JsApplication4、请求

由于本人水平有限,不免有不对或不足的地方,希望大家能够提出,我们共同进步。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180130G05D5S00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券