Android Glide(一)缓存机制

什么是Glide

Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片。这也是Google官方推荐的图片加载框架。

如何使用Glide

Glide.with(this).load(url).into(imageView);
一切就是这么简单。
仅仅就这一行代码,已经可以做非常非常多的事情了,包括加载网络上的图片、加载手机本地的图片、加载应用资源中的图片等等。

我们简单地分析一下这行代码

.with()

用于创建一个加载图片的实例。
with()方法可以接收Context、Activity或者Fragment类型的参数。

.load()

这个方法用于指定待加载的图片资源。
Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载。

.into()

这个方法就很简单了,我们希望让图片显示在哪个ImageView上,把这个ImageView的实例传进去就可以了。
除此之外,
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading) //加载占位图。
.error(R.drawable.error) //加载失败时的占位图
.diskCacheStrategy(DiskCacheStrategy.NONE) //这里是可以选择Glide的缓存策略
.override(100, 100) //指定图片大小
.asGif() //指定图片格式
.into(imageView);

那么回顾一下Glide最基本的使用方式,其实就是关键的三步走:先with(),再load(),最后into()。熟记这三步,你就已经入门Glide了。
没错,就是这么简单。正所谓大道至简。当然我并不是要教大家如何使用Glide,毕竟需要用的人已经会了,不需要用的人也没必要学会。我只是想给你们看看它有多好用。
这个框架用起来实在是太舒服了,简洁明了,一秒种就能入门。
但是,哪有什么岁月静好,不过是有人替你负重前行。
我们今天之所以能够这么简单的加载图片,当然是有人在背后做了成吨的工作。

今天只分析Glide缓存机制

Glide的缓存机制

首先,为什么要缓存?

  • 1、减少流量消耗,加快响应速度;
  • 2、Bitmap 的创建/销毁比较耗内存,可能会导致频繁GC;使用缓存可以更加高效地加载 Bitmap,减少卡顿。

Glide的缓存结构

首先明确一点,Glide是三级缓存。
缓存是内存--->磁盘--->网络这样的方式去获取图片资源的,但这就是3级缓存吗?明显不是,这个只是2级缓存;Glide也是按照这种策略来获取图片的,但是还略有不同,Glide将它的缓存分为2个大的部分,一个是内存缓存,一个是硬盘缓存。其中内存缓存又分为2种,弱引用(正在使用的资源)和Lrucache;磁盘缓存就是DiskLrucache,DiskLrucache算法和Lrucache差不多的,所以现在看起来Glide3级缓存的话应该是WeakReference + Lrucache + DiskLrucache。
内存缓存的主要作用是防止应用重复将图片数据读取到内存当中;而硬盘缓存的主要作用是防止应用重复从网络或其他地方下载和读取数据。

Glide的缓存Key

缓存是为了解决重复加载问题,那必然要有一个key来区分不同的图片资源。决定缓存Key的参数有很多种,其中包括图片URL、宽、高。
这里可以得出一个结论,几乎任意配置的改变都会导致同一张图片生成多个缓存key。
举个例子:同一张图片加载到2个不同大小的ImageView会生成2个缓存图片。

Glide的缓存的读写

前面提到过内存缓存是通过弱引用+LruCache的方式实现的。

  • 弱引用是由一个HashMap维护,key是上述的缓存key;value是图片资源对象的弱引用形式。
  • LruCache是由一个LinkedHashMap维护,根据Lru算法来管理图片。
    大致的原理是利用LinkedHashMap链表的特性,把最近使用过的文件插入到列表头部,没使用的图片放在尾部;然后当图片大小到达预先设置的一个阀值的时候 ,按算法删除列表尾部的部分数据。
  • DiskLruCache也是类似原理。由于篇幅有限,这里不讲解LruCache和DiskLruCache的底层原理,这里推荐一篇文章 https://www.jianshu.com/p/8f4f58b4b8ab

内存缓存原理

  • 读数据
    灵魂画手出马


    未命名.png

在内存缓存中有一个概念叫图片引用资源计数器 ,抽象来说,就是定义一个acquired变量用来记录图片被引用的次数,调用acquire()方法会让变量加1,调用release()方法会让变量减1。
获取图片资源是先从弱引用取缓存,拿到的话,引用计数+1;
没有的话从LruCache中拿缓存,拿到的话,从LruCache中删除这张图片,引用计数也是+1,同时把图片从LruCache缓存转移到弱应用缓存池中;(这里如果再次读取就可以直接从弱引用中获取了)
再没有的话就通过EngineJob开启线程池去加载图片,拿到的话,引用计数也是+1,会把图片放到弱引用。(但这是后话了)

  • 写数据
    很明显,这是加载图片之后的事情。通过EngineJob开启线程池去加载图片,取到数据之后,会回调到主线程,把图片存到弱引用。
    当图片不再使用的时候,比如说暂停请求或者加载完毕或者清除资源时,就会将其从弱引用中转移到LruCache缓存池中。
  • 总结
    正在使用中的图片使用弱引用来进行缓存,暂时不用的图片使用LruCache来进行缓存的功能;同一张图片只会出现在弱引用和LruCache中的一个。这里我们也要注意到先后顺序,先到弱引用,再到LruCache.
  • 这里我们可以关注一个问题:
    为什么要使用弱引用,而不是直接使用LruCache?
    1、分压策略,减少Lrucache 中trimToSize(移除最老的数据)的概率。
    2、提高效率:弱引用用的是HashMap,Lrucache用的是LinkedHashMap,从访问效率而言,肯定是HashMap更高。
    磁盘缓存原理(DiskLruCache)

Glide磁盘缓存策略(4.x)

  • DiskCacheStrategy.DATA: 只缓存原始图片;
  • DiskCacheStrategy.RESOURCE:只缓存转换过后的图片;
  • DiskCacheStrategy.ALL:既缓存原始图片,也缓存转换过后的图片;对于远程图片,缓存 DATA和 RESOURCE;对于本地图片,只缓存 RESOURCE;
  • DiskCacheStrategy.NONE:不缓存任何内容;
  • DiskCacheStrategy.AUTOMATIC:默认策略,尝试对本地和远程图片使用最佳的策略。当下载网络图片时,使用DATA(原因很简单,对本地图片的处理可比网络要容易得多);对于本地图片,使用RESOURCE。

如果在内存缓存中没获取到数据会通过EngineJob开启线程池去加载图片,也就是刚才说的后话。
这里有2个关键类:DecodeJob 和EngineJob。

  • EngineJob 内部维护了线程池,用来管理资源加载,当资源加载完毕的时候通知回调;
  • DecodeJob是线程池中的一个任务。
    磁盘缓存是通过DiskLruCache来管理的,根据不同的缓存策略,会有2种类型的图片,DATA(原始图片)和 RESOURCE(转换后的图片)。磁盘缓存依次通过ResourcesCacheGenerator、SourceGenerator、DataCacheGenerator来获取缓存数据。
    ResourcesCacheGenerator获取的是转换过的缓存数据;
    SourceGenerator获取的是未经转换的原始的缓存数据;
    DataCacheGenerator是通过网络获取图片数据再按照按照缓存策略的不同去缓存不同的图片到磁盘上。

总结

  • Glide缓存分为弱引用+ LruCache+ DiskLruCache,其中读取数据的顺序是:弱引用 > LruCache > DiskLruCache>网络;写入缓存的顺序是:网络 --> DiskLruCache--> 弱引用-->LruCache
  • 内存缓存分为弱引用的和 LruCache ,其中正在使用的图片使用弱引用缓存,暂时不使用的图片用 LruCache缓存,这一点是通过 图片引用计数器(acquired变量)来实现的。
  • 磁盘缓存就是通过DiskLruCache实现的,根据缓存策略的不同会获取到不同类型的缓存图片。
    它的逻辑是:先从转换后的缓存中取;
    没有的话再从原始的(没有转换过的)缓存中拿数据;
    再没有的话就从网络加载图片数据,获取到数据之后,再依次缓存到磁盘和弱引用。
  • 盗图一张,再度回顾一下这个流程。


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

推荐阅读更多精彩内容