本文是在查看源码后然后根据前辈的总结,然后做出关于 Glide 加载图片流程的总结整理。
一、整体的加载流程
但我们加载图片调用Glide.with().load()
时,整体来看Glide 加载图片分为如下几个步骤,当然首次加载图片是有初始化的必要,产生一个单例的Glide 后,后续的加载图片过程就剩下三个步骤了。
初始化Glide
当调用 Glide.with() 时,如果是首次加载图片,会初始化一个 Glide 单例。与此同时,一些相关的部件也会初始化,比如RequestManagerRetriever、Engine、LruResourceCache、BitmapPool、ArrayPool、加载选项、编解码器、转码器等等都会被初始化。此外在实例化 RequestManager 的同时,与无界面的 Fragment 绑定生命周期。
请求构建器RequestBuilder
调用 into() 构建RequestBuilder 时,会根据 ImageView 构建一个ViewTarget 目标,并且与 request实例绑定。默认情况下使用的是 SingleRequest 实例。在ViewTarget 中通过实现
ViewTreeObserver.OnPreDrawListener
接口,监听当 onPreDraw 得到执行时,就会触发 SingleRequest的 onSizeReady 方法,进而触发 Engine 的 load 方法执行加载图片任务解码任务 DecodeJob
Engine 加载图片任务得到执行后,通过 EngineJob中的GlideExecutor来调度执行调用 DecodeJob 加载图片。而DecodeJod 的主要任务则是先后通过内存缓存、磁盘缓存、或者资源地址加载图片。在加载磁盘缓存或者资源地址加载图片时,会根据入参 Model 匹配对应的ModelLoader,获取相应LoaData 加载对应的图片。
目标 Target
在DecodeJob 完成图片的加载后,通过回调方法 onResourceReady() 通知 EngineJob以及 SingleRequest,在SingleRequest 的onResourceReady方法中再通知 ViewTarget 加载图片资源。其中在 EngineJob 中通调用 Engine 的 onEngineJobComplete回调方法,实现对图片资源的 ActiveResources 内存缓存。
二、从 into() 到 DecodeJob.run()
由源码中得知,从构建 Request 请求 into 加载图片到图片显示可分为上图中的10个步骤。其中主要涉及到 5个对象
- 加载请求 Request
- 图片加载目标 Target
- 图片加载引擎 Engine
- 图片加载引擎任务管理 EngineJob
- 解码任务 DecodeJob
2.1、Requet
2.1.1、RequestManager 的创建
通过Glide 调用 with(context)时,实际上会通过RequestManagerRetriver 创建 RequestManager,并且将 RequestManager 与一个无界面的 Fragment 的生命周期进行绑定和监听,方便 RequestManger 管理 Request,实现图片的加载、停止、销毁能够与页面的生命周期相关联。同时也添加了网络状态的监听,让图片的加载、停止也能够更加网络状态的变化做出相应的动作。
创建 RequestManagerRetriver
Glide 首次调用会在Glide初始化时创建 RequestManagerRetriver 实例。
绑定生命周期
在 Glide 中,一个 Context 对应一个 RequestManager。当调用 with(context) 时,通过
RequestManagerRetriver.get(context)
方法获取或者创建一个对应的 RequestManager, 与此同时也会使用context创建一个相对应的无UI界面的FragmentRequestManager 实现了 LifecycleListener接口,意味着能够监听Fragment的生命周期。
Fragment 主要是用了做生命周期的关联,当感知到宿主 Activity/Fragment 的生命周期发生变化时,就会通知 RequestManager, 让它去做暂停请求、继续请求和取消请求等操作。
如果我们用的是 ApplicationContext 加载某张图片,那就意味着这次图片加载操作的生命周期是与应用的生命周期绑定的。(一般使用上不建议图片的加载与应用的生命周期绑定)
监听网络状态
RequestManager 有一个 RequestManagerConnectivityListener 网络监听器,它实现了
ConnectivityMonitor.ConnectivityListener
接口,当网络状态切换时,RequestManager 就会重启所有的图片加载请求。创建请求构造器 RequestBuilder
加载图片调用 load() 设置图片资源来源是 RequestManager的方法,在调用这个方法实际上是创建了 RequestBuilder 构造器,RequestManager 中有很多创建 RequestBuilder 的方法,比如 asDrawable()、asBitmap() 、asFile() 等,这些方法对应着不同泛型参数的 RequestBuilder 。
load() 方法对应的参数有:
- Bitmap
- Drawable
- String
- Uri
- URL
- File
- Integer(resourceId)
- byte[]
- Object
启动请求
RequestManager 的 track() 方法调用了 TargetTracker 的 track() 方法,还调用了请求跟踪器 RequestTracker 的 runRequest() 方法 。
TargetTracker
TargetTracker 实现了 LifecycleListener ,当RequestManager 收到页面生命周期变动时,则通过TargetTracker实例调用相应的方法,使它会根据页面生命周期播放和暂停动画,比如暂停 Gif 动画。RequestTracker
RequestTracker 的 runRequest() 方法调用了 Request.begin() 方法。
在 Request 的 begin() 方法中会获取 View 的尺寸,获取到了尺寸后就会调用 Engine 的 load() 方法启动图片加载请求。
2.1.2、Request 请求构建
调用into() 方法时,会通过 RequestBuilder 构建请求
into()
当发起图片加载请求时,Glide 会吧请求封装为Request,而构建Request 的任务则是由 RequestBuilder 请求建造器完成。
调用into() 方法,实则是调用 RequestBuilder 的into() 方法,
Model
Glide 会把我们在 load() 方法传入的图片数据来源封装为 Model ,对应为 RequestBuilder 中的Object类型的model对象。
2.1.3、Request 的6种状态
待运行 PENDING
创建 SingleRequest 后,该 Request 就进入待运行状态
已清除 CLEARED
每次我们用 into() 方法加载图片时,RequestManager 都会先看下我们传入的 Target 是否有对应的 Request ,如果有的话就会调用该 Request 的 clear() 方法释放资源,这时 Request 就进入了已清除状态。
待测量 WAITING_FOR_SIZE
当 RequestManager 调用 RequestTracker 的 runRequest() 方法后,RequestTracker 就会调用 Request 的 begin() 方法,这时请求就进入了待测量状态。
运行中 RUNNING
在 SingleRequest 的 begin() 方法中,调用了 Target 的 getSize() 方法获取 ImageView 的尺寸,获取到尺寸后,SingleRequst 会调用 Engine 的 load() 方法启动图片加载请求,这时 Request 就进入了运行中状态。
已完成 COMPLETE
当 Engine 从内存中加载到资源,或者通过解码任务加载到资源后,就会调用 SingleRequest 的 onResourceReady() 方法,这时 Request 就进入了已完成状态。
失败 FAILED
当解码任务 DecodeJob 在处理图片的过程中遇到异常时,就会调用 EngineJob 的 onLoadFailed() 方法,然后 EngineJob 会调用 SingleRequest 的 onLoadFailed() 方法,这时 SingleRequest 就进入了失败状态。
2.1.4、三种占位图
使用Glide 加载图片是,我们可以设置 placeholder、error 和 fallback 三种占位图
placeholder
图片加载完成前显示的占位图;
error
图片加载失败时显示的占位图;
fallback
图片来源为空时显示的占位图;
使用占位图时,要注意占位图是不会使用 Transformation 进行变换的,如果你想弄个圆角或圆形的占位图,可以用 submit().get() 获取对应变换后的占位图的 Drawable 对象,然后传到对应的占位图设置方法中。
2.1.5、Request 的相关问题
我们平时用 Glide 加载图片调用的 into() 方法是哪个类的方法?
当设备的网络状态发生变化时,是谁负责重启图片加载请求?
Request 有哪几种状态?这些状态是如何流转的?
Glide 有几种占位图?分别在什么时候显示?
2.2、Target
当调用 RequestBuilder 的 into() 方法将 ImageView 作为参数传进去后,Glide 将 ImageView 转换包装为Target,下面将会根据源码总结分析不同 Target 的作用。
2.2.1、ImageViewTarget
SizeDeterminer
ImageViewTarget 继承自 ViewTarget,ViewTarget 中有一个用来获取View尺寸的 SizeDeterminer,其中通过 getSize(SizeReadyCallback) 设置回调拿到尺寸,是把 ImageView 的内边距 padding() 去掉后的尺寸。
在 Glide 中,宽高分为请求宽高和原始宽高 ,而 SizeDeterminer 拿到的尺寸就是请求宽高,Glide 会根据请求宽高对图片进行缩放操作,以减少不必要的内存消耗。OnPreDrawListener
当 Request 获取 View 的尺寸失败时,ViewTarget 会通过 ViewTreeObserver 的 OnPreDrawListener 的回调来获取 View 的尺寸,然后再传给 Request。
setResource()
ImageViewTarget 主要有 BitmapImageViewTarget 和 DrawableImageViewTarget 两个子类,它们两个的区别就在于它们的 setResource() 方法。
BitmapImageViewTarget
setResource() 用的是 ImageView 的 setImageBitmap() 方法;DrawableImageViewTarget
setResource() 用的是 ImageView 的 setImageDrawable() 方法;
2.2.2、RequestFutureTarget
submit()
FutureTarget 是一个实现了 Future 和 Target 接口的接口,它只有一个 RequestFutureTarget 子类 ,当我们用 submit() 方法获取 Glide 加载好的图片资源时,就是创建了一个 RequestFutureTarget 。Waiter
RequestFutureTarget 是用 wait/notify 的方式来实现等待和通知的,这两个是 Object 的方法,Request 中有一个 Waiter ,当 DecodeJob 加载到图片后,RequestFutureTarget 就会让 Waiter 发出通知,这时我们的 get() 方法就能获取到返回值了。
这就是为什么我们用 RequestFutureTarget 的 get() 方法获取图片时,要把这个操作放在子线程运行。
2.2.3、 CustomTarget
给不是 View 的 Target 加载图片时,Glide 都把它作为 CustomTarget 。
- PreloadTarget
预加载 Target 。
当我们调用 preload() 选项预加载图片时,Glide 会把图片交给 PreloadTarget 处理,当 PreloadTarget 接收到图片资源后,就会让 RequestManager 把该请求的资源释放掉。
因为不需要等待资源加载完成,所以我们在用 preload() 预加载图片时,不用像 submit() 一样在子线程中执行。 - AppWidgetTarget
桌面组件 Target 。
当 AppWidgetTarget 接收到处理好的图片资源后,会把它设置给 RemoteView ,然后通过桌面组件管理器 AppWidgetManager 更新桌面组件。 - DelayTarget
GifTarget。
这是加载 Gif 图片时要用到的 Target ,关于 Glide 加载 Gif 图片的流程在后面会讲到。 - NotificationTarget
通知栏 Target 。
这个 Target 有一个 setBitmap 方法,会把图片设置给通知栏的 RemoteView ,然后通过 NotificationManager 更新通知栏中的通知。
2.2.4、Target 的相关问题
ImageViewTarget 是用什么来获取请求宽高的?
为什么在用 submit() 获取图片时,要放在子线程中执行?
使用 preload() 预加载图片时,用的是哪个 Target ?该 Target 获取到资源会后做什么?
2.3、Engine
下面一起看看关于 Engine 的相关实现
Engine 的作用
Key 的作用
Resource 的作用
BitmapPool
2.3.1、Engine 的作用
Engine 是 Glide 的图片加载引擎,是 Glide 中非常重要的一个类,其大致流程如下图所示
Engine.load()
由前面的分析可知,当调用 into() 后,会构建一个 Request,进而触发
Request.begin()
方法,在获取到 Target 的尺寸后,回调 onSizeReady,然后触发调用Engin.load()
方法。在 load() 方法中,Engine 会先调用 EngineKeyFactory 创建资源标识符 Key,然后用这个 Key 去内存缓存中加载资源。
如果从内存中找到了资源,Engine 就会直接把资源回传给 Resource,如果没有加载到资源,Engine 就会创建并启动新的 EngineJob 和解码任务 DecodeJob。
**EngineKeyFactory **
EngineKeyFactory 是 Engine 中负责生产 EngineKey 的工厂,EngineKey 是引擎任务资源标识符,关于什么是 Key 后面进一步讲。
在 Engine 启动新的任务加载图片前,会先通过 EngineKeyFactory 创建一个 EngineKey,然后让 DecodeJob 把资源与 EngineKey 进行绑定,这里说的绑定,其实就是把 model 放到 EngineKey 中。
回收资源
Engine 中有一个资源回收器 ResourceRecycler ,Resource 接口中有一个 recycle() 方法,关于 Resource 我们后面再讲。
这里只要知道,当 SingleRequest 被清除,比如在 into() 方法中发现 Target 已经有对应的 Request 时,Request 就会让 Engine 释放资源,具体做释放资源操作的就是 ResourceRecycler。磁盘缓存提供器
LazyDiskCacheProvider 是 Engine 中的一个静态内部类,是磁盘缓存 DiskCache 的提供器,DiskCache 是一个接口,关于 DiskCache 的实现我们后面再讲。
启动新的解码任务
当 Engine 从内存中找不到对应的 Key 的资源时,就会启动新的解码任务。
Engine 会用加载任务工厂 EngineJobFactory 构建一个加载任务 EngineJob,然后再构建一个解码任务 DecodeJob。
EngineJob 这个名字看起来很霸气,但是实际上它并没有做什么事情,它只是 Engine 与 DecodeJob 之间沟通的桥梁。
当构建了 EngineJob 和 DecodeJob 后,Engine 就会把 DecodeJob 提交到线程池 GlideExecutor 中。
2.3.2、Key
前面讲到了 Engine 会通过 EngineKeyFactory 创建资源标识符 Key ,那什么是 Key ?
Key 是 Glide 中的一个接口,是图片资源的标识符。
避免比较有误
Glide 的内存缓存和磁盘缓存用的都是 Glide 自己实现的 LruCache,LruCache 也就是最近最少使用缓存算法(Least Recently Used),LruCache 中有一个 LinkedHashMap ,这个 HashMap 的 Key 就是 Key 接口,而 Value 则是 Resource 接口。
在用对象作为 HashMap 的 Key 时,要重写 equals() 和 hashCode() 方法。
如果不重写这两个方法,那么当两个 Key 的内存地址不同,但是实际代表的资源相同时,使用父类 Object的 hasCode() 直接用内存地址做比较,那么结果会是不相等。
此外 Object 的 equals() 方法也是拿内存地址作比较,所以也要重写。
比如下面就是 ResourceCacheKey 的 equals() 判断逻辑。Key 的实现类
DataCacheKey
原始图片数据标识符。ResourceCacheKey
处理后的图片资源标识符。AndroidResourceSignature
Android 资源标识符。当我们传入 into() 方法的图片是 R.drawable.xxx 时,Glide 就会把它封装为 AndroidResourceSignature 。ObjectKey
通用资源标识符。
可以说除了 App 自带的 Android 资源以外的图片资源都会用 ObjectKey 作为标识符,比如本地图片文件。EngineKey
引擎资源标识符。
这个 Key 是 Engine 对其他 Key 的封装,这时传进来的 Key 是以签名(Signature)的身份存在 EngineKey 中的。
2.3.3、Resource
前面讲到了 Engine 会通过 ResourceRecycler 来回收资源,而 ResourceRecycler 调用了 Resource 的 recycle() 方法。
可能你想起来 Bitmap 就有一个可以回收图片内存的 recycle() 方法,没错,Glide 回收 Bitmap 的方式就是用的 Bitmap 自带的 recycle() 方法,但是这个过程又比这复杂一些。
Resource 是一个接口,其中一个实现类是 BitmapResource ,也就是位图资源,比如网络图片就会转化为 BitmapResource。
在 BitmapResource 中有一个位图池 BitmapPool,这是 Glide 用来复用 Bitmap 的一个接口,具体的实现类是 LruBitmapPool 。
在 BitmapResource 的 recycle() 方法中,会把对应的 Bitmap 通过 put() 方法放到 BitmapPool 中,关于 BitmapPool 在讲 Glide 缓存原理时会进一步讲。
2.3.4、Engine 相关问题
Engine 的 load() 方法首先会做什么?
为什么 Key 要重写 hashCode() 和 equals() 方法?
负责回收 Resource 的是哪个类?
加载 Drawable 资源时,会转化为哪种 Key?
2.4、EngineJob
EngineJob 可以看做 Engine 的一个附属工具类,主要用于启动 DecodeJob, 同时也充当着回调分发的任务,当DecodeJob 加载图片成功后回调给 EngineJob。EngineJob 则把接接结果同时分发会 Engine 和 SingleRequest
通过调用 onEngineJobComplete() 可以把图片资源返回给 Engine
通过调用 onResourceReady() 可以把加载成功的图片返回给 SingleRequest
2.5、DecodeJob
在前面的分析总结中,当从内存缓存中获取不到图片资源是,则启动解码任务。在DecodeJob 解码任务中,如果在磁盘缓存中获取不到图片,那么就从图片源地址中获取图片。
DecodeJob 实现了 Runnable 接口,EngineJob 启动 DecodeJob 的方式就是把它提交给 GlideExecutor,如果我们没有调整磁盘缓存策略的话,那默认用的就是 diskCacheExecutor ,关于 GlideExecutor 在第 4 大节会讲,下面我们先看下 DecodeJob 的实现。
2.5.1、runWrapped()
从源码中分析,DecoedJob.run() 方法调用了 runWrapped(),并且只对其异常捕获。而 runWrapped() 根据不同的 RunReason 运行不同的 DataFetcherGenerator。
三种不同的运行理由 RunReason:
INITAILIZE
从缓存中获取数据并解码;SWITCH_TO_SOURCE_SERVICE
从来源获取数据后再进行解码;DECODE_DATA
当获取数据的线程与 DecodeJob 的线程不同时,比如使用了 OkHttp-Integration 时,DecodeJob 会直接对数据进行解码;初始化
Decode 初始化 init() 为默认状态,会通过 ResourceCacheGenerator 实例化DataFetcherGenerator,获取磁盘中经过变换后的图片资源。如果获取不到变换后的图片资源则再通过 DataCacheGenerator 获取原始图片数据缓存。
从数据来源获取数据
当从磁盘缓存中获取不到图片资源后,则runReason 变更为 SWITCH_TO_SOURCE_SERVICE,从来源获取数据,然后运行来源数据生成器 SourceGenerator
对检索到的数据进行解密
DecodeJob 通过数据生成器获取到数据后,就会调用 decodeFromRetrievedData() 方法来对检索到的数据进行解码。此时runReason 变更为 DECODE_DATA
2.5.2、DecodeJob 的数据获取流程
在 getNextState() 方法中,根据当前的解码步骤来判断下一步应该执行的步骤,总共分为 6 个阶段,这两个阶段是 State 枚举类中的值,分别如下:
INITIALIZE :
初始化,默认状态。
最开始解码处于这个状态,然后 DecodeJob 会根据缓存策略,判断是否要从磁盘中获取处理过图片数据缓存资源,如果是,则 生成
ResourceCacheGenerator
实例获取图片资源。如果获取到数据,则进行解码。如果获取不到处理过的图片数据资源,那么就切换到下一个解码阶段 RESOURCE_CACHE。RESOURCE_CACHE:
从磁盘缓存中获取处理过的图片,其对应的缓存资源Generator 为
ResourceCacheGenera
。当解码处于当前阶段时, DecodeJob 会根据缓存策略判断是否要从磁盘中获取 未处理过的图片数据,如果是的话就用 DataCacheGenerator获取数据。
DATA_CACHE:
从缓存中获取原始(未处理过的)数据。
当处于该阶段时,如果在加载图片时调用了 onlyRetrieveFromCache(true) ,那么就不会切换到 SOURCE 阶段从来源获取数据,而直接切换的 FINISHED 阶段结束解码流程。
SOURCE:
从图片来源获取原始数据。
如果 DecodeJob 在 RESOURCE_CACHE 和 DATA_CACHE 阶段都没有拿到图片数据,那就会用 SourceGenerator 从图片来源获取图片数据。
ENCODE :
编码
当磁盘缓存策略设定了要对图片资源进行缓存时,那么在获取到数据后,DecodeJob 就会用 ResourceDecoder 对资源进行编码,也就是把图片放到磁盘缓存中。
FINISHED : 结束
2.5.3、三种数据生成器
在DecodeJob 中调用 getNextGenerator() 方法,在不同的阶段,根据缓存策略,分别创建和使用不同的数据生成器:
- ResourceCacheGenerator : 缓存数据生成器
- DataCacheGenerator : 原始缓存数据生成器
- SourceGenerator: 来源数据生成器
这三种数据生成器都实现了 DataFetcherGenerator 接口,同时也实现 DataFetcher.DataCallback 接口
DecodeJob 依赖于 DataFetcherGenerator接口,通过 DataFetcherGenerator.FetcherReadyCallback 回调接口来获得数据加载。
DataFetcherGenerator 会通过 ModelLoader 构建数据封装对象 LoadData ,然后通过 LoadData 中的 DataFetcher 来加载数据,此外数据生成器也实现了 DataFetcher.DataCallback 接口,用于接收 DataFetcher 的数据回调。
LoadData 是 ModelLoader 的内部类,拥有 数据来源标识符 Key 和 数据加载器 DataFetcher 两个成员变量。
SourceGenerator 逻辑流程图如下
步骤分析:
是否获取到了需要缓存的数据
当 SourceGenerator 加载完数据后,会再次进入 startNext() 方法,这时就获取到了需要缓存的数据。
是否保存原始数据
如果磁盘缓存策略设定了要保存图片的原始数据,就用数据提取器加载数据,否则就直接把图片加载给 Target 。
加载数据
当需要保存原始数据或数据有加载路径时,SourceGenerator 就会根据 Model 的类型,使用对应的 DataFetcher 来提取数据,比如从网络上下载图
是否保存原始数据
当 SourceGenerator 获取到数据后,会再次判断是否要保存原始数据,否则就直接把图片加载给 Target 。
编码
当 SourceGenerator 从 DataFetcher 中拿到数据后,会再走一遍 startNext() 方法,然后用编码器 Encoder 对数据进行编码,也就是把图片放到磁盘缓存中。
从磁盘中获取数据
当 SourceGenerator 把数据保存到磁盘后,不会直接加载图片,而是从磁盘中拿这张图片,然后再进行加载。
2.5.4、onResourceDecoded() 变换应用
在获取到图片资源 Resource 后,最终会调用 DecodeJob.onResourceDecoded() 方法,然后应用变换选项和初始化编码管理器。
应用变换选项
对于已经处理过变换的图片不再进行变换,否则就对图片进行变换操作
回收图片资源
当对资源应用了变换之后,DecodeJob 会把原来的资源回收掉,因为原来的图片资源用不上。
缓存变换后的图片资源
根据磁盘缓存策略判断是否要对资源进行编码,如果要编码,则根据不同的编码策略创建不同的Key。Glide 有 SOURCE 和 TRANSFORMED 两种编码策略,分别对应 原始资源编码 和 变换后资源编码。
SOURCE : GIF 编码器 对应GifDrawableEncoder 中用的编码策略
TRANSFORMED : 位图编码器 BitmapEncoder 中用的编码策略
初始化编码管理器
创建好 Key 后不会直接对图片进行编码,而是会修改编码管理器的 Key ,等到转码完成后再用 ResourceEncoder 进行编码。
2.5.5、DecodeJob相关问题
DecodeJob 会根据哪些理由来执行任务?
DecodeJob 提取数据的过程分为哪几个阶段?
DataFetcherGenerator 有哪些实现类?
Glide 有几种编码策略?
三、6步图片加载
在总结梳理了DecodeJob 界码流程后,接下来是图片的加载。在获取到图片后,大致经过如上图所示的6个步骤,解码、变换,转码,加载图片到 target,然后编码等。
图片的解码过程涉及以下几个概念:
数据来源(Model)
Glide 会以 Model 的形式封装图片来源 ,Model 可以是 URL、本地文件和网络图片等类型。
原始数据(Data)
Glide 把数据源转换为Model 后,会把它加工成原始数据 Data ,一般就是输入流 InputStream ,Glide 会把这些输入流封装为 Data ,而 ModelLoader 则负责从 Data 获取原始数据。
资源(Resource)
获取到原始数据后,Glide 会用资源解码器 ResourceDecoder 对原始数据进行解码,比如把输入流 InputStream 解码为 Bitmap,解码后的资源就是 Resource 。
变换后资源(TransformedResource)
Glide 会根据我们的变换选项处理 Resource ,比如用 centerCrop() 裁剪就是一种变换,变换后的 Resource 就叫 TransformedResource ,负责转换的就是 Transformation 。
转码后资源(TranscodedResource)
Glide 除了能加载静态图片,还能加载 Gif 动态图,解码后的 Bitmap 和 Gif 的类型不是统一的,为了统一处理静态和动态图片,Glide 会把 Bitmap 转换为 GlideBitmapDrawable ,而负责转码的角色则是
目标(Target)
Glide 最终会把图片显示到目标 Target 上,比如 ImageView 对应的就是 ImageViewTarget 。
3.1、ModelLoader
ModelLoader 是一个负责创建 LoadData 的接口,它有两个泛型参数 Model 和 Data。
Model 代表图片来源的类型,比如图片的网络地址的 Model 类型为 String ;
Data 代表图片的原始数据的类型,比如网络图片对应的类型为 InputStream ;
Factory
在 DataFetcherGenerator 获取图片数据时,会调用 ModelLoaderRegistry 的 getModelLoaders() 方法,这个方法中会根据 model 的类型用 MultiModelLoaderFactory 生成对应的 ModelLoader,比如能够解析字符串的 ModelLoader 就有 7 个,关于 ModelLoaderRegistry 在后面讲 Glide 配置的时候会讲到。
此外每一个 ModelLoader 的实现类中都定义了一个实现了 ModelLoaderFactory 接口的静态内部类 。handles()
一个 Model 对应这么多 ModelLoader,每个 ModelLoader 加载数据的方式都不同,这时候就要用 handles() 方法了。
ModelLoader 接口有 handles() 和 buildLoadData() 两个方法,handles() 用于判断某个 Model 是否能被自己处理,比如 HttpUriLoader 的 handles() 会判断传进来的字符串是否以 http 或 https 开头,是的话则可以处理。builLoadData()
ModelLoader 之间是存在嵌套关系的,比如 HttpUriLoader 的 buildLoadData() 方法就是调用的 HttpGlideUrlLoader 的 buildLoadData() 方法,HttpGlideUrlLoader 会创建一个 HttpUrlFetcher ,然后把它放到 LoadData() 中。
LoadData 是 ModelLoader 中定义的一个类,它只是放置了图片来源的 Key 和要用来提取数据的 DataFetcher ,没有其他方法。
3.2、ResourceDecoder
DataFetcherGenerator 使用 ModelLoader 构建完数据后,就会用 DataRewinder 对数据进行重绕,也就是重置数据,比如 InputStreamRewinder 就会调用 RecyclableBufferedInputStream 的 reset() 方法重置输入流对应的字节数组的位置。
ResourceDecoder 是一个接口,有非常多的实现类,比如网络图片对应的解码器为 StreamBitmapDecoder ,StreamBitmapDecoder 的 decode() 方法调用了降采样器 Downsampler 的 decode() 方法,下图是 Downsampler 的解码逻辑。
设置目标宽高
除非我们通过 override() 方法把尺寸改为 Target.SIZE_ROGINAL ,否则 Glide 默认会把 ImageView 的大小作为加载图片的目标宽高。计算缩放后宽高
根据不同的变换选项计算缩放后宽高。创建空 Bitmap
根据计算后的目标宽高创建一个空的 Bitmap 。使用 BitmapFactory 解码
Downsampler 的解码方式用的是 ImageReader 的 decodeBitmap() 方法,而 ImageReader 又调用了 BitmapFactory 的 decodeStream() 方法,BitmapFactory 最终调用的是 SkImageDecoder 的 decode() 方法。
把 Bitmap 放入 BitmapPool 中
在前面讲 Resource 的时候讲到了 BitmapResource 中有一个 BitmapPool,这个 BitmapPool 是由 Downsampler 传过去的,而 Downsampler 的 BitmapPool 是由 Glide 创建并传进来的。
3.3、Transformation
Transformation 是一个接口,它有一个 transform() 方法,这个方法是在 DecodeJob 中调用的,当 DecodeJob 发现数据源不是缓存中的 Resource 时,就会调用变换选项的 transform() 方法。
Transformation 的其中一个实现类是 BitmapTransformation,我们平时调用的 centerCrop() 就是 BitmapTransformation 的子类,centerCrop() 选项对应的是 CenterCrop 类,它实现了 Transformation 接口,具体的变换实现在 TransformationUtils 中。
Matrix
以 centerCrop() 为例,TransformationUtils 的 centerCrop() 方法会先创建一个 Matrix 矩阵,然后根据传进来的 Bitmap 计算 Matrix 的缩放比例和平移坐标。
drawBitmap()
配置好 Matrix 后,就会根据目标宽高创建一个空的目标 Bitmap ,然后把原始 Bitmap、目标 Bitmap 和 Matrix 传给 Canvas 的 drawBitmap() 方法,然后返回 Canvas 处理好的图片。
3.4、ResouceTranscoder
ResourceTranscoder 是一个接口,是 Glide 中的资源转码器,它有两个泛型参数 Z 和 R ,分别代表需要进行原始类型和转码目标类型。
比如 BitmapDrawableTranscoder 的原始类型是 Bitmap,转码目标类型是 BitmapDrawable,在BitmapDrawableTranscoder 的 transcode() 方法中,会把 Bitmap 转换为 BitmapDrawable ,以便 Target 进行处理。
2.5、ResourceEncoder
ResourceEncoder 是一个接口,是 Glide 中的资源编码器,ResourceEncoder 有好几个实现类,比如网络图片对应的编码器为 StreamEncoder。
在转码完成后,DecodeJob 会先把图片加载到 Target 中,然后用 ResourceEncoder 对图片进行编码,比如 StreamEncoder 的编码操作就是把输入流 InputStream 转化为图片文件,然后保存到本地。