目录
Glide简介
Glide是Google推荐的一套快速高效的图片加载框架,作者是bumptech,功能强大且使用方便,实际的android应用开发中,有不少的开发者在使用它,今天,就带大家来讲解下Glide的使用及实现的逻辑流程。
地址: Glide源码地址
Glide的优点
- 使用方便,API简洁。with、load、into三步就可以加载图片
- 生命周期自动绑定,根据绑定的Activity或者Fragment生命周期管理图片请求
- 高效的缓存策略,两级内存,两级文件
A. 支持Memory和Disk图片缓存
B. Picasso 只会缓存原始尺寸的图片,而 Glide 缓存的是多种规格,也就意味着 Glide 会根据你 ImageView 的大小来缓存相应大小的图片尺寸.
C. 内存开销小
默认的 Bitmap 格式是 RGB_565 格式,而 Picasso 默认的是 ARGB_8888 格式,这个内存开销要小一半。
比如你 ImageView 大小是200200,原图是 400400 ,而使用 Glide 就会缓存 200200 规格的图,而 Picasso 只会缓存 400400 规格的。这个改进就会导致 Glide 比 Picasso 加载的速度要快,毕竟少了每次裁剪重新渲染的过程,非常灵活 & 加载速度快
Android关于图片内存计算,共有四种,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存(默认,色彩最细腻=显示质量最高=占用的内存也最大)
RGB_565:每个像素占用2byte内存(8bit = 1byte)
举例说明:一个32位的PNG=ARGB_8888=1204x1024,那么占用空间是:1024x1024x(32/8) = 4,194,304kb=4M左右
在解析图片的时候,为了避免oom和节省内存,最好使用ARGB_4444模式(节省一半的内存空间)
- 支持多种图片格式(Gif,Webp,Jpg)
- 有效的内存管理机制
Glide的生命周期
参考下面文章详解,这里只做总结
Android:深入了解图片加载库Glide的生命周期管理(源码分析)
with()方法就是用于绑定生命周期的,with()方法的重载种类非常多,既可以传入Activity,也可以传入Fragment或者是Context。调用RequestManagerRetriever的静态get()方法得到一个RequestManagerRetriever对象,这个静态get()方法就是一个单例实现,然后再调用RequestManagerRetriever的实例get()方法,去获取RequestManager对象。
RequestManagerRetriever的实例get()方法,传入什么Context参数,Activity参数,Fragment参数等等,实际上只有两种情况而已,即传入Application类型的参数,和传入非Application类型的参数。
- Application参数:如果传入的是Application对象,那么这里就会调用带有Context参数的get()方法重载,调用getApplicationManager()方法来获取一个RequestManager对象。其实这是最简单的一种情况,因为Application对象的生命周期即应用程序的生命周期,因此Glide并不需要做什么特殊的处理,它自动就是和应用程序的生命周期是同步的,如果应用程序关闭的话,Glide的加载也会同时终止。
- 非Application参数:不管传入的是Activity、FragmentActivity、v4包下的Fragment、还是app包下的Fragment,最终的流程都是一样的,那就是会向当前的Activity当中添加一个隐藏的Fragment。因为Glide需要知道加载的生命周期。很简单的一个道理,如果你在某个Activity上正在加载着一张图片,结果图片还没加载出来,Activity就被用户关掉了,那么图片还应该继续加载吗?当然不应该。可是Glide并没有办法知道Activity的生命周期,于是Glide就使用了添加隐藏Fragment的这种小技巧,因为Fragment的生命周期和Activity是同步的,如果Activity被销毁了,Fragment是可以监听到的,这样Glide就可以捕获这个事件并停止图片加载了。
- 如果我们是在非主线程当中使用的Glide,那么不管你是传入的Activity还是Fragment,都会被强制当成Application来处理。
Glide如何实现图片缓存的
参考:Android:深入剖析图片加载库Glide缓存功能(源码分析)
- Glide的缓存功能设计成 二级缓存:内存缓存 & 硬盘缓存
并不是三级缓存,因为从网络加载 不属于缓存
- 缓存读取顺序:内存缓存 --> 磁盘缓存 --> 网络
1.内存缓存 默认开启
2.Glide中,内存缓存 & 磁盘缓存相互不影响,独立配置
-
二级缓存的作用不同:
- 1.内存缓存:防止应用重复将图片数据 读取到内存当中
只缓存转换过后的图片
-
- 2.硬盘缓存:防止应用重复从网络或其他地方重复下载和读取数据
可缓存原始图片 & 缓存转换过后的图片,用户自行设置
Glide的缓存机制使得 Glide具备非常好的图片缓存效果,从而使得具备较高的图片加载效率。
如,在 RecyclerView 上下滑动,而RecyclerView中只要是Glide加载过的图片,都可以直接从内存中读取 & 展示,从而不需要重复从 网络或硬盘上读取,提高图片加载效率。
三级缓存or二级缓存?
在没学习源码之前,我连这个最基本的概念都不确定,以前老是听人说缓存是内存—>磁盘—>网络这样的方式去获取图片资源的,但这就是3级缓存吗?明显不是,这个只是2级缓存;Glide也是按照这种方式获取图片的,但是略有不同,Glide将它的缓存分为2个大的部分,一个是内存缓存,一个是硬盘缓存。其中内存缓存又分为2种,弱引用和Lrucache;磁盘缓存就是DiskLrucache,DiskLrucache算法和Lrucache差不多的,所以现在看起来Glide3级缓存的话应该是WeakReference + Lrucache + DiskLrucache。
内存缓存实现原理
Glide的内存缓存实现是基于:LruCache 算法(Least Recently Used) & 弱引用机制
1.LruCache算法原理:将 最近使用的对象用强引用的方式存储在LinkedHashMap中 ;当缓存满时 ,将最近最少使用的对象从内存中移除
2.弱引用:弱引用的对象具备更短生命周期,因为当JVM进行垃圾回收时,一旦发现弱引用对象,都会进行回收(无论内存充足否)
磁盘缓存实现原理
使用Glide 自定义的DiskLruCache算法
1.该算法基于 Lru 算法中的DiskLruCache算法,具体应用在磁盘缓存的需求场景中
2.该算法被封装到Glide自定义的工具类中(该工具类基于Android 提供的DiskLruCache工具类
Glide5大磁盘缓存策略
- DiskCacheStrategy.DATA: 只缓存原始图片;
- DiskCacheStrategy.RESOURCE:只缓存转换过后的图片;
- DiskCacheStrategy.ALL:既缓存原始图片,也缓存转换过后的图片;对于远程图片,缓存 DATA和 RESOURCE;对于本地图片,只缓存 RESOURCE;
- DiskCacheStrategy.NONE:不缓存任何内容;
- DiskCacheStrategy.AUTOMATIC:默认策略,尝试对本地和远程图片使用最佳的策略。当下载网络图片时,使用DATA;对于本地图片,使用RESOURCE;
如果在内存缓存中没获取到数据会通过EngineJob开启线程池去加载图片,这里有2个关键类:DecodeJob 和EngineJob。EngineJob 内部维护了线程池,用来管理资源加载,当资源加载完毕的时候通知回调; DecodeJob是线程池中的一个任务。
磁盘缓存是在EngineJob中的DecodeJob任务中完成的,依次通过ResourcesCacheGenerator、SourceGenerator、DataCacheGenerator来获取缓存数据。ResourcesCacheGenerator获取的是转换过的缓存数据;SourceGenerator获取的是未经转换的原始的缓存数据;DataCacheGenerator是通过网络获取图片数据再按照按照缓存策略的不同去缓存不同的图片到磁盘上。
引入缓存的目的
- 减少流量消耗,加快响应速度;
Bitmap 的创建/销毁比较耗内存,可能会导致频繁GC; - 使用缓存可以更加高效地加载 Bitmap,减少卡顿。
Glide缓存流程
Glide缓存分为:内存缓存和磁盘缓存,其中内存缓存是由弱引用 + Lrucache组成
从内存缓存读取总结
从磁盘缓存读取总结
写入磁盘缓存
- Glide将图片写入磁盘缓存的时机:获取图片资源后 、图片加载完成前
- 写入磁盘缓存又分为:将 原始图片 写入或将 转换后的图片写入磁盘缓存
写入内存缓存
Glide 将图片写入 内存缓存的时机:图片加载完成后 、图片显示出来前
- 当 acquired 变量 >0 时,说明图片正在使用,即该图片缓存继续存放到activeResources弱引用缓存中
- 当 acquired变量 = 0,即说明图片已经不再被使用,就将该图片的缓存Key从 activeResources弱引用缓存中移除,并存放到LruResourceCache缓存中
至此,实现了:
- 正在使用中的图片 采用 弱引用 的内存缓存
- 不在使用中的图片 采用 LruCache算法 的内存缓存
汇总
用一张图将整个Glide 的图片缓存流程汇总
- 关于内存缓存 的总结
读取 内存缓存 时,先从LruCache算法机制的内存缓存读取,再从弱引用机制的 内存缓存 读取
写入 内存缓存 时,先写入 弱引用机制 的内存缓存,等到图片不再被使用时,再写入到 LruCache算法机制的内存缓存
- 关于磁盘缓存 的总结
读取 磁盘缓存 时,先读取 转换后图片 的缓存,再读取 原始图片 的缓存
是否读取 取决于 Glide使用API的设置
写入 磁盘缓存 时,先写入 原始图片 的内存缓存,再写入的内存缓存
是否写入 取决于 Glide使用API的设置
Glide源码总结图解
参考:Android:手把手带你深入图片加载库Glide源码分析
with()
-
定义:Glide 类中的静态方法,根据传入 不同参数 进行 方法重载
-
作用:
1.得到一个RequestManager对象
2.根据传入with()方法的参数 将Glide图片加载的生命周期与Activity/Fragment的生命周期进行绑定,从而实现自动执行请求,暂停操作
load()
-
定义
由于 .with() 返回的是一个RequestManager对象,所以 第2步中调用的是 RequestManager 类的 load() -
作用
预先创建好对图片进行一系列操作(加载、编解码、转码)的对象,并全部封装到 DrawableTypeRequest对象中。
into()
- 作用:
构建网络请求对象 并 执行 该网络请求
即 获取图片资源 & 加载图片并显示
- 总体逻辑如下:
- 详细过程:
在第2步的RequestManager的load()中,最终返回一个DrawableTypeRequest对象
封装好了对图片进行一系列操作(加载、编解码、转码)的对象
但 DrawableTypeRequest类中并没有load()和第3步需要分析的into(),所以load() 和 into() 是在DrawableTypeRequest类的父类中:DrawableRequestBuilder类
继承关系如下
所以,第三步是调用DrawableRequestBuilder类的 into()完成图片的最终加载
总结
一图总结Glide的基本功能图片加载的全过程