探寻Glide,你可能需要知道的知识点

441 阅读7分钟

我正在参加「掘金·启航计划」

当我们去面试的时候,当面试官问道项目中我们是如何去加载图片的?相信绝大多数同学都会回答Glide ,作为目前常用的图片加载框架Glide,这个时候面试官就会继续往下挖掘,那么它究竟相对其他图片加载框架(Picasso,ImagerLoader等等)有什么优势呢,或者说它的内存管理是怎么样的等等,通过Glide这个图片加载框架进行延展,面试官更想考察大家对于内存管理,缓存的理解

本文通过阅读学习它的源码以及查阅相关资料,解答下面给出的几个问题,相信各位同学会对Glide有了一个初步的认知

  • 为什么Glide不需要担心内存泄漏?
  • Glide是如何监听页面的生命周期?
  • Glide有几级缓存,分别说说?
  • Glide缓存命中的流程是怎么样的?

为什么Glide不需要担心内存泄漏

我们要知道,一般出现内存泄漏的原因,无非就是图片内存占用过大,长生命周期引用了短生命周期的对象等等;另外,这个问题其实可以从 Glide与其他图片加载框架的区别,以及它是怎么处理内存作为切入点

传统图片缓存方式

首先我们来看下传统的图片加载框架(ImageLoader),举个栗子,假如有10张图片需要加载,然后会开辟一定容量的内存用来存储对应的bitmap,当有新的图片需要缓存的时候,但是内存已经没有空间了,此时就会把那个最近最久没有使用过的bitmap销毁掉,再将新的bitmap填充进来;它就是采用LRUCache算法(FIFO先进先出置换算法)进行图片缓存,有着命中快,加载效率高的优势

传统图片加载框架.png

那么这种方式的劣势就很明显了,这个缓存容量是有限的,当达到上限的时候就必须要移除部分数据来添加新的内容,也就意味一旦我们去缓存了相当容量的图片,当我们不再显示这些图片的时候,但是内存上依旧存在这些图片的空间,其实是不合理的,这就造成了App长期运行内存居高不下,这种情况下很容易就会出现OOM

Glide是如何处理的

在这之前要明确一个问题,什么时候应该把图片缓存到内存中呢?即要保证命中快又要内存少,所以Glide采取了折中的方法,在用户需要看到的时候,把图片缓存下来,反之就不缓存下来;这样依来就解决了传统图片框架内存居高不下的弊端;显而易见,这个实现方式最大的问题就是需要监听页面的生命周期,因为我们要知道这个页面是否要加载图片;

通过分析Glide的源码,它在加载资源的时候,如果是在 ActivityFragment 这一类有生命周期的组件上进行的话,会创建一个透明的 RequestManagerFragment 加入到FragmentManager 之中,感知生命周期,当 ActivityFragment 等组件进入不可见,或者已经销毁的时候,Glide 会停止加载资源。但是如果,是在非生命周期的组件上进行时,会采用Application 的生命周期贯穿整个应用,所以 applicationManager 只有在应用程序关闭的时候终止加载。

通过以上分析,这就是为什么Glide不太需要担心内存泄漏的原因了

Glide是如何监听页面的生命周期

这个问题在上面讨论内存泄漏的问题的时候就已经说过了,Glide它优秀的地方就是可以去监听页面的生命周期,我们知道FragmentActivity的生命周期是绑定的,所以它在这一类组件进行的话,会经历如下步骤:

  • 创建一个透明的RequestManagerFragment加入到RequestManager
  • 通过RequestManager实例去监听各个组件的接口实例ActivityFragmentLifcycle
  • Activity或者Fragment不可见,或者被销毁的时候,Glide就会停止加载资源

Glide有几级缓存,分别说说?

一般来说是有3级缓存,分别是活动缓存,内存缓存,磁盘缓存,如果算上网络缓存的话,可以说有四级缓存

  • 活动缓存:简单来说,它就是页面缓存下来的图片,用户可见的,也可以叫做弱引用缓存

  • 内存缓存:这个是遵从LRU算法来管理缓存对象,对于那些计数器不为0的图片进行缓存,目的是防止应用重复读取同一张图片到内存,造成内存资源浪费

  • 磁盘缓存:比较之下,它可以缓存大量的图片,但是相对来说磁盘IO会比较慢,目的是防止应用重复的从网络或者其他地方下载和读取数据

glide四级缓存.png

Glide缓存命中的流程是怎么样的?

  • Glide在加载某张图片的时候,首先会去内存缓存(Lrucache缓存)中寻找是否有,如果有的话旧把它取出来使用,此时引用计数acquire加1,并将它放入到弱引用缓存中,如果LruCache缓存中没有,则去弱引用缓存中寻找,如果弱引用缓存中有,则从弱引用缓存中取出图片使用,此时引用计数acquire加1,如果弱引用缓存中也没有图片,则从磁盘缓存/网络中加载图片。

  • 如果没有这张图片的时候,先从网络中下载下来,然后将它放入到磁盘缓存中,接着将图片放入到弱引用中,Glide中是有个引用计数器,它会记录图片被引用的次数,通过其中的acquire变量来判断是否使用弱引用/Lrucache缓存;当它大于0的时候,此时说明图片在使用中,这时候将图片放到弱应用缓存中;当它等于0的时候,说明图片已经不再使用了,这时候会去释放资源,内部先将缓存的图片从弱引用中移除,然后放入到Lrucache中;

    总的来说就一句话,Glide实现了对于正在使用的图片使用弱引用缓存,不再使用的图片使用Lrucache缓存的功能

  • Glide存储图片的时候,缓存顺序如下:

活动缓存(弱引用缓存)-->内存缓存---> 磁盘缓存

  • Glide读取图片的时候,缓存顺序如下:

内存缓存---->活动缓存--->磁盘缓存

缓存命中流程.png 简单总结下,就是当我们使用Glide去加载图片的时候,第一次需要从网络下载图片,然后保存到磁盘缓存当中,之后不管加载多少次,只需要从活动缓存当中就可以获取相应图片资源;

当然以上前提都是建立在用户一直在当前页面,保证图片可见的情况下,如果退出从当前界面返回回去的时候,就会对该资源以及活动缓存进行释放销毁;在下一次进入该页面加载图片的时候就是从内存缓存中读取了,再之后和之前一样都是从活动缓存中获取,如下图所示,当图片在不同情况下调用的时候,Glide是如何进行加载图片的,以及进行缓存命中的流程

glide缓存流程.png