探索 Glide 原理

code小生 一个专注大前端领域的技术平台公众号回复Android加入安卓技术群

作者: 灯不利多

链接:https://juejin.im/post/6882536990400020494

声明:本文已获 灯不利多授权发表,转发等请联系原作者授权

前言

1. Glide 基本用法

接下来的讲解将基于 Glide 目前的最新版本 4.11。

Glide 的使用特别简单,首先添加依赖。

然后调用下面这三个方法。

  • with()

    可以传 Applicaiton、Activity 、Fragment 与 view 等类型的参数,加载图片的请求会与该参数的生命周期绑定在一起。

  • load()

    可以传图片的网络地址、Drawable 等。

  • into()

    一般传 ImageView 。

2. 内容概览

Glide 加载图片大致可分为三个步骤。

  1. 发起加载图片请求

    当我们用 into() 方法加载图片时,就是发起了一次图片加载请求;

  2. 执行解码任务

    我们在 load() 方法中设置的图片来源会传到 DecodeJob 中,DecodeJob 就会被 Engine 提交到线程池中开始执行;

  3. 加载图片

    当 DecodeJob 对图片解码完成后,就会把图片加载到 ImageView 中;

接下来会以这三个步骤为基础来展开 Glide 的图片加载流程,下面是每个大节讲解的内容。

1. 四步启动解码任务

第一大节将会讲解从我们调用 into() 方法到启动 DecodeJob 的 run() 方法的过程中,Glide 做了哪些事情,包含了 Request、Target 、 Engine 和 DecodeJob 等内容。

2. 六步加载图片

第二大节会讲解当 DecodeJob 获取到图片数据后,会怎么处理图片数据,也就是解码、加载图片和编码的过程,包括 ModelLoader、ResourceDecoder、Transformation、ResourceTranscoder 以及 ResourceEncoder 的实现。

3. Glide 缓存原理

这里会讲 Glide 的图片缓存相关的实现,包括内存缓存、磁盘缓存、BitmapPool 以及 ArrayPool 等内容。

4. Glide 初始化流程与配置

这一节会讲解 Glide 的初始化流程,包括 Glide 调用配置的方式、AppGlideModule 的两个方法中用到的 Registry 和 GlideBuilder 在 Glide 中的作用。

5. Glide 图片加载选项

Glide 开放了非常多的图片加载选项,我们不一定全都用得上,但是了解这些选项,可以让我们在需要的时候能调用对应的选项,不用再自己实现一遍。

1. 四步启动解码任务

这一节我们先来看下从 into() 到启动 DecodeJob 的过程中涉及哪些对象。

从我们调用 into() 方法加载图片到启动解码任务 DecodeJob,大致分为 4 个步骤,涉及下面 4 个对象。

  1. 加载请求 Request

  2. 加载目标 Target

  3. 加载引擎 Engine

  4. 解码任务 DecodeJob

下面我们围绕这 4 个对象看看 Glide 的解码任务的启动流程。

1.1 Request

1.1.1 请求构建器 RequestBuilder
1. into()

对于我们发起的图片加载请求,Glide 会把这个请求封装为 Request,而 RequestBuilder 就是 Request 的构建器。

当我们用 into() 方法加载图片时,调用的其实是 RequestBuilder 的 into() 方法,这个方法会创建一个 Request ,并把 Request 传给请求管理器。

2. Model

Glide 会把我们在 load() 中传入的图片来源数据封装为 Model ,而这个 Model 具体就是 RequestBuilder 中的 model 字段,类型为 Object 。

3. 加载选项

RequestBuilder 继承了 BaseRequestOptions 抽象类,我们平时用的 centerCrop() 等方法大部分都是 BaseRequestOptions 的方法,关于图片加载选项在后面会讲到。

1.1.2 请求管理器 RequestManager

RequestManager 有下面几个特点。

  • 绑定生命周期

  • 监听网络状态

  • 创建请求构建器

  • 启动请求

1. 绑定生命周期

在 Glide 中,一个 Context 对应一个 RequestManager,当我们调用 with() 方法时,RequestManager 会用对应的 Context 创建一个 RequestManagerFragment 。

RequestManagerFragment 是一个无布局的 Fragment,主要是用来做生命周期关联的,当这个 Fragment 感知到 Activity 的生命周期发生变化时,就会告诉请求管理器,让它去做暂停请求、继续请求和取消请求等操作。

如果我们用的是 ApplicationContext 加载某张图片,那就意味着这次图片加载操作的生命周期是与应用的生命周期绑定的。

2. 监听网络状态

RequestManager 中有一个网络连接监听器 RequestManagerConnectivityListener ,它实现了ConnectivityListener 接口,每次网络状态切换时,RequestManager 就会重启所有的图片加载请求。

3. 创建请求构建器

我们在加载图片时调用的 load() 方法是 RequestManager 的方法,调用这个方法其实是创建了一个请求构建器 RequestBuilder,RequestManager 中有很多创建 RequestBuilder 的方法,比如 asDrawable()、asBitmap() 、asFile() 等,这些方法对应着不同泛型参数的 RequestBuilder 。

load() 方法支持下面这些类型的参数。

  • Bitmap

  • Drawable

  • String

  • Uri

  • URL

  • File

  • Integer(resourceId)

  • byte[]

  • Object

4. 启动请求

RequestManager 的 track() 方法调用了目标跟踪器 TargetTracker 的 track() 方法,还调用了请求跟踪器 RequestTracker 的 runRequest() 方法 。

  • TargetTracker

    TargetTracker 实现了 LifecycleListener ,它会根据页面生命周期播放和暂停动画,比如暂停 Gif 动画。

  • RequestTracker

    RequestTracker 的 runRequest() 方法调用了 Request.begin() 方法。

    在 Request 的 begin() 方法中会获取 View 的尺寸,获取到了尺寸后就会调用 Engine 的 load() 方法启动图片加载请求。

1.1.3 Request 的 6 种状态

前面讲到的 Request 具体就是 SingleRequest ,SingleRequest 中有一个 Status 枚举类,包含了请求的 6 种状态。

1. 待运行 PENDING

当我们通过 into() 创建了一个 SingleRequest 后,该 Request 就进入了待运行状态。

2. 已清除 CLEARED

每次我们用 into() 方法加载图片时,RequestManager 都会先看下我们传入的 Target 是否有对应的 Request ,如果有的话就会调用该 Request 的 clear() 方法释放资源,这时 Request 就进入了已清除状态。

3. 待测量 WAITING_FOR_SIZE

当 RequestManager 调用 RequestTracker 的 runRequest() 方法后,RequestTracker 就会调用 Request 的 begin() 方法,这时请求就进入了待测量状态。

4. 运行中 RUNNING

在 SingleRequest 的 begin() 方法中,调用了 Target 的 getSize() 方法获取 ImageView 的尺寸,获取到尺寸后,SingleRequst 会调用 Engine 的 load() 方法启动图片加载请求,这时 Request 就进入了运行中状态。

5. 已完成 COMPLETE

当 Engine 从内存中加载到资源,或者通过解码任务加载到资源后,就会调用 SingleRequest 的 onResourceReady() 方法,这时 Request 就进入了已完成状态。

6. 失败 FAILED

当解码任务 DecodeJob 在处理图片的过程中遇到异常时,就会调用 EngineJob 的 onLoadFailed() 方法,然后 EngineJob 会调用 SingleRequest 的 onLoadFailed() 方法,这时 SingleRequest 就进入了失败状态。

1.1.4 三种占位图

我们在加载图片时,可以设置 placeholder、error 和 fallback 三种占位图。

  • placeholder

    图片加载完成前显示的占位图;

  • error

    图片加载失败时显示的占位图;

  • fallback

    图片来源为空时显示的占位图;

使用占位图时,要注意占位图是不会使用 Transformation 进行变换的,如果你想弄个圆角或圆形的占位图,可以用 submit().get() 获取对应变换后的占位图的 Drawable 对象,然后传到对应的占位图设置方法中。

1.1.5 Request 相关问题

下面是几个跟 Request 相关的问题,看看你能不能答得上来。

  1. 我们平时用 Glide 加载图片调用的 into() 方法是哪个类的方法?

  2. 当设备的网络状态发生变化时,是谁负责重启图片加载请求?

  3. Request 有哪几种状态?这些状态是如何流转的?

  4. Glide 有几种占位图?分别在什么时候显示?

1.2 Target

当我们调用 into() 方法,传入 ImageView 后,Glide 会把 ImageView 转化为 Target ,下面我们来看下不同 Target 的作用。

1.2.1 ImageViewTarget
1. SizeDeterminer

ImageViewTarget 继承了 ViewTarget ,在 ViewTarget 中有一个用来获取尺寸的 SizeDeterminer ,SizeDeterminer 的 getSize() 方法拿到的尺寸,是把 ImageView 的内边距 padding() 去掉后的尺寸。

在 Glide 中,宽高分为请求宽高和原始宽高 ,而 SizeDeterminer 拿到的尺寸就是请求宽高,Glide 会根据请求宽高对图片进行缩放操作,以减少不必要的内存消耗。

2. OnPreDrawListener

当 Request 获取 View 的尺寸失败时,ViewTarget 会通过 ViewTreeObserver 的 OnPreDrawListener 的回调来获取 View 的尺寸,然后再传给 Request。

3. setResource()

ImageViewTarget 主要有 BitmapImageViewTarget 和 DrawableImageViewTarget 两个子类,它们两个的区别就在于它们的 setResource() 方法。

  • BitmapImageViewTarget

    setResource() 用的是 ImageView 的 setImageBitmap() 方法;

  • DrawableImageViewTarget

    setResource() 用的是 ImageView 的 setImageDrawable() 方法;

1.2.2 RequestFutureTarget
1. submit()

FutureTarget 是一个实现了 Future 和 Target 接口的接口,它只有一个 RequestFutureTarget 子类 ,当我们用 submit() 方法获取 Glide 加载好的图片资源时,就是创建了一个 RequestFutureTarget 。

2. Waiter

RequestFutureTarget 是用 wait/notify 的方式来实现等待和通知的,这两个是 Object 的方法,Request 中有一个 Waiter ,当 DecodeJob 加载到图片后,RequestFutureTarget 就会让 Waiter 发出通知,这时我们的 get() 方法就能获取到返回值了。

这就是为什么我们用 RequestFutureTarget 的 get() 方法获取图片时,要把这个操作放在子线程运行。

1.2.3  CustomTarget

给不是 View 的 Target 加载图片时,Glide 都把它作为 CustomTarget 。

1. PreloadTarget

预加载 Target 。

当我们调用 preload() 选项预加载图片时,Glide 会把图片交给 PreloadTarget 处理,当 PreloadTarget 接收到图片资源后,就会让 RequestManager 把该请求的资源释放掉。

因为不需要等待资源加载完成,所以我们在用 preload() 预加载图片时,不用像 submit() 一样在子线程中执行。

2. AppWidgetTarget

桌面组件 Target 。

当 AppWidgetTarget 接收到处理好的图片资源后,会把它设置给 RemoteView ,然后通过桌面组件管理器 AppWidgetManager 更新桌面组件。

3. DelayTarget

GifTarget。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值