欢迎关注我的其他平台账号:
掘金:0xforee
个人博客:0xforee’s blog
微信公众号:
上期我们探索了一个具备核心功能的图片加载库该怎么设计。这一期我们来看看如何给这个图片加载库关联生命周期管理。
欢迎关注本系列其他文章:
- 从代码设计看 Glide 之写在开头
- 从代码设计看 Glide 之核心功能
- 从代码设计看 Glide 之生命周期(上)
- 从代码设计看 Glide 之生命周期(中)
- 从代码设计看 Glide 之生命周期(下)
用过 Glide 一定不会对这几个方法陌生
Glide#with(android.content.Context)
Glide#with(android.app.Activity)
Glide#with(FragmentActivity)
Glide#with(Fragment)
Glide#with(android.app.Fragment)
Glide#with(android.view.View)
那你知道为什么 Glide 要绑定 Context,Activity,Fragment 或 View 吗?
让图片加载库感知和联动生命周期有个非常大的好处,就是可以让库的管理从手动档变为自动档。
上一期讲到的图片加载库的核心功能中,包括了一些例如 Request 请求管理,图片加载线程管理等的功能,如果我们按照这个设计去实现,势必要面临一个问题。页面退出时,该如何处理这些可能正在加载的请求。
一种普遍的处理方案是,我们对外提供结束请求,释放内存,释放引用的接口,在每个页面销毁的时候,手工调用框架接口完成管理。
如果用过 Android 中 Handler 的一定知道,我们要在每个页面退出的时候,remove 掉所有可能还未执行的请求,防止内存泄露。
所以如果框架可以自动帮我们结束请求,释放内存,释放引用就好了。就像我们从 C 语言迈入 Java 的那样,不再需要手动管理内存。而我们这期所关注的生命周期就可以做到这些。
总结来说,框架拥有生命周期的能力是为了:
接下来我们看看 Glide 是如何实现生命周期管理的?并且它是怎么设计的?
说起 Lifecycler 大家一定会想到 Google 官方为 Activity 和 Fragment 提供的库 android.lifecycle
2018年,Jetpack 横空出世,带来了一系列新的特性,其中就包括 Lifecycle 生命周期的库。这个库可以帮我们掌握 Activity 和 Fragment 的生命周期。让我们实现更加智能的框架。
生命周期的介绍可以参考这篇官方指引文档
Lifecycle 这个库 Android 内部也有很多地方用。比如最近几年很火的库:LiveData,其中就用了 Lifecycle,从而让数据绑定和释放更加智能。
还有系统代码 SystemUI 模块-快捷开关的部分,系统设置中的 WifiTracker 模块等等。可以说在有了 Lifecycle 库后,系统的很多功能都用 Lifecycle 重写了一遍。
以下是使用到 Lifecycle 的一些系统包示例
com.android.systemui.statusbar.phone
com.android.systemui.statusbar.policy
com.android.systemui.util
com.android.wifitrackerlib
好了,那我们就用 Lifecycle 库来探究一下如何为之前设计的图片加载库增加生命周期识别的能力。
生命周期涉及两个角色:
那么提供者如何把自己的生命周期告诉 RequestManager 呢?
没错。简单来说就是回调
并且因为提供者需要通知多个监听者,所以这里需要提供者维护一个监听者队列。比如这里可以用观察者模式或者订阅者模式等。
监听者通过 addObserver 把自己加到 LifecycleOwner 维护的列表中来感知生命周期。
大概是这样:
如果我们的模块比较简单这么实现也完全没问题,但 Activity 这个类要负责的事项就重多了,如果还得负责维护 Lifecycle 注册相关的一堆事项,代码会变得更加庞杂且不优雅,所以我们将这部分逻辑拆出来单独交给一个新的角色 Registry 维护。
大概会变成这样:
Registry 会专门负责维护注册 Observer 和取消注册 Observer 以及分发生命周期状态变化这些职责。
整体的使用流程是:
Client 通过 Activity 取得 Lifecycle 的实现类 LifecycleRegistry 之后,通过调用 LifecycleRegistry.addObserver()
添加一个 LifecycleObserver 的实现类 DefaultLifecycleObserver,我们就可以在 DefaultLifecycleObserver 中获取到 activity 的生命周期了。
当然,实际还比这个要复杂一点点。因为 Lifecycle 不仅支持注册回调监听的方式,还支持注解监听的方式。
但我们这期主要是讲 Lifecycle 的使用,Lifecycle 具体的实现我们就不展开了。如果大家感兴趣这块的代码设计思路,可以评论告诉我,后边安排上。
弄明白 Lifecycle 各个角色和它的职责之后,我们看看我们的需求。这里我们使用最简化的模型来理解(降低对不重要模块的细节关注度)
我们需要让 RequestManager 来感知到 Lifecycle 的状态变化,从而实现 Request 的自动启动和停止,达到智能释放引用的目标。
所以,我们让 RequestManager 实现 LifecycleObserver,然后调用 LifecycleRegister.addObserver()
把自己加进去,就可以实现生命周期的感知了。
真的是非常简单,来个类图。(基于第二章核心功能的类图所绘,忘记了的回去补一下课)
_
不过,不能高兴的太早了。我们回顾一下这个类图,看看有什么遗漏的地方?
想一想,我们打开一个包含多个 Fragment 的 Activity 页面的时候,会有几个 Lifecycle?
我们在上期完成的图片加载库,RequestManager 的生命周期和 Application,Glide 是一致的,他们之间是 1:1 的关系。
当我们将能力拓展到 Activity 和 Fragment 的时候,Glide 本身生命周期没有变化,但是 RequestManager 生命周期却和 Glide 不一致了,变成了 n:1 的关系。
所以类图变成了这样:
注意 Glide 和 RequestManager 之间变成了聚合的关系
此时,Glide 需要持有一个 Map,来保存 Lifecycle 和 RequestManager 的对应关系,这样对于相同的 Lifecycle 可以复用 同样的 RequestManager,从另外一个角度来说,具有相同生命周期的 Request 们都聚合到了一个 Lifecycle 下。
我们再进一步优化一下
在我们上边 Lifecycle 简单版和复杂版的示例过程中,我们是将注册管理 LifecycleObserver 的部分从 Activity(或 Fragment )中拆出来了,是为了让 Activity 更好专注于自己的本职工作,也为了减少 Activity 本身的复杂度。
同样,Glide 作为对外的入口类,我们需要保持它的整洁和易用,专注在提供对外的简单和易用接口。所以我们同样将注册管理 RequestManager 的这部分逻辑拆出来。
新增加一个 RequestManagerRetriver 管理类角色,来根据 Lifecycle 构建 RequestManager。我们考虑到除了真正就有 Lifecycle 生命周期的 Activity 和 Fragment 外,还有一些场景会需要用到全局的生命周期去加载一些图片请求,所以我们将 Manager 的类一分为二:
类图变成了这样
Glide 将所有 RequestManager 的创建委托给了 RequestManagerRetriever,通过 RequestManagerRetriever.get()
方法可以获取到各个类型的 Context 所对应的 RequestManager。
其中 RequestManagerRtriever 内部又将生命周期相关的分为 Application 生命周期和其他生命周期。
这样,对于Application来说,总的请求都会交给 RequestManagerRtriever.applicationManager 来处理。
而对于其他的生命周期来说,另外委托给了 LifecycleRequestManagerRetriver,用 Android的 Lifecycle 做 Key 来持有 RequestManager。例如 Context 是 Activity 的时候,会根据 Activity 所对应的生命周期的 Lifecycle 来获取对应的 RequestManager,从而达到复用的目的。
整体是不是看起来清爽多了,各个类的职责分明,每个类也不会太过复杂导致难以理解。
我们来和上期的类图合并一起看看,有忘记的同学也可以借此回顾一下。
Glide 生命周期的代码设计其实不算特别复杂,但是因为多了一些兼容性的逻辑,以及一些编程思想在里边,所以解析的时候要照顾到方方面面的话内容还是挺多的。你应该也从标题中猜出来了,生命周期的内容还未完待续。主要是为了保证篇幅不至于过长,大家理解起来轻松一些。剩下的部分我们预计会分为 1-2 期来完成,敬请期待 _
写完这段话的时候,已经凌晨 2 点了。想到第二天还要上班就开始掉头发。但生活还得继续不是吗。看到这里的你也要加油啊。我们下期见。
(如果你觉得这篇文章写得还不错,点个赞,点个收藏,点个关注吧。深夜码字不易)