本文已同步发表于个人博客:0xforee’s blog

前言

接上文。这篇咱们首要关注于 Glide 的中心才能,来看一看构建出 Glide 中心的才能的模块或者类是哪些。

示例

假如用过 Glide 必定记住经过 Glide 加载一个图片最简略的写法是什么。

Glide.with(applicationContext).load("https://xx.image/test").into(view)
加载某张图片到某个 View。

这就是 Glide 最中心的代码。

Glide 中其他的代码和模块都是围绕在这个中心链路周围的。比方经过 load 能够拓展出不同的数据来源,经过 Target 拓展出不同的展现目标。还比方说给 load 的所要加载的数据添加缓存,给 load 本身添加生命周期等等。

一切的这一切都是在加载图片到 View 上这个中心功用根底之上的。

所以,咱们这篇文章就从这个中心代码开端。

正文

Glide 的 with 传入的是 Android 的 Context,Glide 首要用 Context 来做生命周期办理。所以本文咱们先不考虑 Context 的实例究竟是 Activity 仍是 Fragment,咱们就默许他只能为大局的 Application(横竖本章也不会讲到^_^),关于 Activity 和 Fragment 相关的内容,咱们会在后续的生命周期华章中胪陈。

从简略的图片加载功用开端

咱们先抛开文章最初的 Glide 示例,来思考:假如让你完结一个最简略的图片加载功用,将网络图片加载到 View 上,你会怎么写?

咱们的思路必定是:

  1. 下载网络图片
  2. 下载完结后,经过回调将图片设置到 View 上

完结方案就是将下载封装到一个类中,然后给出一个回调就行了。

假如咱们再考虑多一些:要一同加载的链接或许会有多个,要一同加载的 View 也或许会有多个的情况下,一个类或许就有点放不下了。

咱们恰当拆点责任出来,将每个下载进程都封装成独立的恳求(Request),然后运用一个类(RequestManager)一致办理

那么新的图片加载功用就包括有这几个责任了

  • Glide:一致对外的类
  • Request:封装单个下载恳求的类,会经过 into 关联到咱们要加载的 Target
  • RequestManager:办理多个恳求的类,咱们经过 With 来获取 RequestManager
  • Target:图片要加载到哪个 View 的抽象类(例如 View 或许是 TextView 也或许是 ImageView)

咱们来画一个简略的类图描述一下这几个类的联系

其间 RequestManager 和 Request 是聚合的联系(注意聚合联系和组合联系的区别)

从代码设计看 Glide 之核心功能

结构中心逻辑尽量内聚

假如这个时分咱们想对 Request 恳求做一些参数修正和定制的话,这个调用链有个当地就不适宜了。

Request 是咱们结构内的原始的恳求抽象,是不能随意对外暴漏出去,由于这样会影响到咱们结构的稳定性。即使运用者有修正定制 Request 的需求,咱们也要让运用者依照咱们束缚的参数来运用,而规划模式中有一个模式十分适合做这个事情。

Builder : 你是在说我吗?你一定是在说我。

(由于 Glide 中太多 Builder 和他的兄弟 Factory 了,所以后边有一期会专门讲 Glide 中 Factory 和 Buidler 的典范,咱们能够多多关注点赞一下)

那么咱们在这儿添加新的一个人物,RequestBuilder,代替 Request 放到调用链中,便利咱们对 Request 参数做一些定制。当时,他只要一个功用就是接管了 Request 的构建进程,经过 into 将 Target 传递进来

现在,咱们的类图变成这样了

从代码设计看 Glide 之核心功能

这个时分,假如咱们去看示例中 Glide 每一段调用链所返回的目标,你会发现,和咱们上图中所展现的如出一辙。

咱们已经有了一个开端能够运用的图片加载功用版本了。

Request 表明我很难

但实际咱们在写的时分,不会将图片下载的悉数进程都要交给 Request 来做,由于这个责任太重了,会导致 Request 类变得特别大,后边假如要做缓存,网络库解耦,本地加载等功用的时分,也会频频修正到这个类,不符合规划准则中的开闭准则

因此咱们把图片下载和加载的这部分责任从 Request 拆出来,等咱们图片下载和加载准备安排妥当了再告诉 Request 就好,让 Request 专一和 Target(View) 打交道。

关于下载和加载的部分咱们这儿就简略规划一个使命办理类,内部包括一个线程池,能够支持多使命调度下载加载,并且在每个使命的图片加载结束后经过回调告诉 Request。

依据这个需求咱们再新添加 2 个人物和 1 个回调:

  • Engine:担任办理下载使命的运转和调度
  • EngineJob:是下载图片和加载图片的使命抽象
  • ResourceCallback:EngineJob 加载资源完结后,经过这个回调告诉 Request。

好了,咱们在类图中再加几员大将,类图现在如下:

从代码设计看 Glide 之核心功能

但…

图片尺度怎么办?

假如你直接就开端依照咱们上边规划的去完结,你会发现有一个问题:

咱们应该在什么时分去调用结构去做图片加载呢,之所以考虑调用的机遇是由于这会联系到两个问题:

  1. View 是不是准备好要展现了?(比方 Visibility.GONE 的时分加载图片干什么?嫌电量掉的不行快吗?)

  2. 图片应该加载多大尺度呢?

也就是说,最好有个回调 SizeReadyCallback 能够告诉咱们开端加载的机遇,让咱们能够触发咱们真实的资源加载,而不至于在 view 本来不打算展现的场景下,还会继续触发加载。

关于 Android 来说,要感知到这个机遇,有一个名为 OnPreDrawListener 的回调能够帮到咱们

一者,在 onPreDraw 触发的时分,预示着 view 行将要被展现出来了。
二者,能够经过 onPreDraw 不断检测当时 view 的尺度是否合法了。

这样等咱们能够获取到合法的尺度之后,就经过 SizeReadyCallback 回调给 Request,推进 Request 开端做真正的恳求。

那这个 ResourceCallback 谁来调用呢?当然是咱们的 Target 了(view),由于他能够知道自己什么时分能真的准备好。

好了,那咱们再这个回调弥补到咱们的类图中

从代码设计看 Glide 之核心功能

额定弥补

假如你直接去看 Glide 主流程的调用链路,盯着 request 一直往下走,会发现在 request 启动之后,没有后续了。那么真正的 load 在哪里开端的,request 并没有体现。

咱们会在 Request 的完结 SingleRequest 中看到一个不起眼的代码

target.getSize(this);

SingleRquest 将自己注册给 Target,而 Target 则监听了 Android 的 ViewObserver

ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this); 
observer.addOnPreDrawListener(layoutListener);

在 PreDraw 的时分,Target 就会不断检测当时 View 的尺度是否合法了。这样等尺度准备好之后,就经过 SizeReadyCallback 回调告诉 Request,推进 Request 开端做真正的恳求。

最终检查

看到这儿,咱们已经完结了自己规划的一个简易图片加载库的中心功用了(其实是Glide的),咱们最终以面向目标的思想来看一下每个类的责任:

  • Glide: 是咱们作为图片加载库的总入口,触发图片加载和其他一些功用。一同单例完结。运用 Facade Pattern 规划模式,将内部杂乱的运用逻辑封装为简易的运用接口,下降运用者的接入本钱。
  • RequestManager:恳求办理者,RequestManager 依据 load 的具体内容创立 RequestBuilder,来让用户进一步定制 Request 的细节。一同,RequestManager 也担任多个 Request 的办理,例如启动暂停等。
  • RequestBuilder: 将用户传入的参数(例如 Target)和其他 options 一同构建出 Request
  • Request: 是链接加载到图片的这一行为的抽象。便利咱们把控这个进程。而每个 Request 完结
    • SizeReadyCallback 接口:这个标识真实应该触发 load 的机遇
    • ResourceCallback 接口:这个标识资源加载完结,能够设置到 View 的机遇
  • Engine:下载图片和加载图片使命的办理类,供给线程池的才能,便利异步加载和调度系统资源。
  • EngineJob:下载图片和加载图片的使命抽象。
  • SizeReadyCallback 接口:只要 target 才知道的本身尺度准备好的机遇(比方 view 要在 onPreDraw 才知道自己的尺度大小),所以这个 Callback 需求 target 回调
  • ResourceCallback 接口:图片下载 job 能够知道图片资源什么时分安排妥当,这个 Callback 需求 Job 回调

最终的最终,咱们将其一切的功用简略分为三个模块:

从代码设计看 Glide 之核心功能

结束语

至此,咱们第一个章节内容就结束了。

咱们从 Glide 最根底的图片库的中心才能开端,打造出了一个简易的图片结构。包括了基本的图片加载主流程。

当然,这个图片结构拓展性不行,加载的数据很单一,另外缓存才能也缺失,也无法做生命周期办理,或许会形成内存泄漏等等之类的。当然咱们后边的系列文章会逐步完善这个结构。

下一期,咱们将为这个结构添加生命周期的才能,让这个结构能够完结自我办理。