Android Jetpack 学习笔记之 Lifecycle
本文介绍 LifecycleOwner 的主要概念和基本用法。
ProcessLifecycleOwner
先来看看 ProcessLifecycleOwner
。
为什么先说这个类呢?相信大家可能都有开发这样的一个常见需求:监听当前应用前后台切换,并对此进行响应。这个功能并不复杂,实现 Application.ActivityLifecycleCallbacks
接口即可。问题在于,在 Android 上并没有标准的方法来判断是应用是处于前台还是后台,即使使用 ActivityLifecycleCallbacks
接口,多多少少有些 trick 在其中,所以大部分代码写得并不优雅易读。
而 ProcessLifecycleOwner
,可以被视为 Android 上一个比较标准的监听应用前后台切换的方式。老习惯,先翻译一遍文档。
这个类提供整个应用进程的生命周期。
你可以将这个 LifecycleOwner 作为全体 Activity 的 LifecycleOwner,除了Lifecycle.Event.ON_CREATE
事件只会分发一次,而Lifecycle.Event.ON_DESTROY
永远不被分发。其他事件的分发遵守以下规则:
ProcessLifecycleOwner 会在第一个 Activity 经历Lifecycle.Event.ON_START
和Lifecycle.Event.ON_RESUME
事件时分发这些事件。 ProcessLifecycleOwner 会在最后一个 Activity 经历Lifecycle.Event.ON_PAUSE
和Lifecycle.Event.ON_STOP
事件 延迟一段时间 分发这些事件。这个延时足够长以确保 ProcessLifecycleOwner 不会在 Activity 由于配置变更而销毁重建期间发送任何事件。
这个类非常适用于对 app 前后台状态切换时进行响应、且对生命周期不要求毫秒级准确性的场景。
再来看看 ProcessLifecycleOwner
的用法。
1 | class ForegroundMonitorActivity : AppCompatActivity() { |
这段代码使用 ProcessLifecycleOwner
很优雅地实现了如下功能:
- 应用切换到前台时,打印日志并弹出 toast “goto foreground”
- 应用切换到后台时,打印日志并弹出 toast “goto background”
Lifecycle 库
Lifecycle 的优势
lifecycle 组件的意义在于更容易组织代码。写过 Android 代码的都知道,常常需要在 Activity/Fragment 的生命周期回调中执行一些操作。比如说:
onCreate()
中初始化某些资源onDestory()
中释放某些资源
所以 Activity/Fragment 的代码很容易就越写越多。
1 | class MyActivity extends AppCompatActivity { |
Activity/Fragment 代码多本身并不是问题,真正的问题在于正确性和可测试性。以上述代码为例,它就不能保证正确性和可测试性。
- 先说正确性。如果
Util.checkUserStatus()
是一个耗时的异步操作,等到真正调用到myLocationListener.start()
时可能 Activity 已经处于停止状态,所以无法保证正确性 - 再说可测试性。写在 Activity/Fragment 中的代码基本是无法做单元测试的。
来看 lifecycle 组件是如何解决正确性和可测试性问题的。修改后的代码如下:
1 | class MyActivity extends AppCompatActivity { |
修改后的代码有这几个好处:
MyLocationListener
可感知生命周期,将生命周期的响应与 Activity 解耦MyLocationListener
易于测试MyLocationListener
可以避免在不正确的生命周期时进行回调
Lifecycle 简介
lifecycle 组件包括 Lifecycle
, LifecycleOwner
, LifecycleObserver
三个主要类:
- Lifecycle 类持有 Fragment 和 Activity (LifecycleOwner) 的生命周期状态信息,并且允许其他对象(LifecycleObserver)观察这些信息。
- 此外 Lifecycle 还使用 Event 和 Status 来记录组件的生命周期状态
- LifecycleOwner 仅有一个
getLifecycle()
方法,用于返回当前对象的Lifecycle
。Fragment 和 Activity 实现了LifecycleOwner
接口 - LifecycleObserver,这个接口通常由 app 来实现,用于封装那需要感知生命周期的对象或业务逻辑
官方文档是这样描述三者的关系的:
LifecycleOwner 接口将 Lifecycle 的所有权从单独的类 (Fragment 和 Activity) 中抽象出来。其他的类也可以实现 LifecycleOwner 接口
实现了 LifecycleObserver 接口的组件可以跟实现了 LifecycleOwner 接口的类很好地协同工作,owner 可以提供 lifecycle,而 observer 则注册到 owner 提供的 lifecycle 进行观察
官方给出的关于 lifecycle 的最佳实践:
- UI controller 尽可能”瘦”
- 编写数据驱动的 UI,UI controller 的作用仅仅是在数据更新时渲染界面
- 数据逻辑放到 ViewModel 中
- 使用数据绑定技术保持 view 和 UI controller 之间有干净的接口
- 如果 UI 过于复杂,考虑使用 presenter 模式
- 避免在 ViewModel 中持有 View 或 UI controller
官方给出的一些应用场景:
- 在精确定位和粗略定位之间切换。使用 lifecycle-aware 组件进行切换,当应用处于前台时使用精确空位,当应用处于后台时切换到粗略定位
- 停止或开始视频流缓冲。使用 lifecycle-aware 组件尽早开始缓冲,但直到 app 完全启动后才开始播放
- 开始或停止网络连接。使用 lifecycle-aware 组件,当应用在前台时开启网络数据,在后台时则停止
- 停止或开始 animated drawable。使用 lifecycle-aware 组件,当应用在前台时播放 animated drawable,在后台时则停止
If a library provides classes that need to work with the Android lifecycle, we recommend that you use lifecycle-aware components. Your library clients can easily integrate those components without manual lifecycle management on the client side.
早期版本的 Glide (v3.7.0之前) 就已经意识到 lifecycle-aware 问题,所以它抽象出以下两个接口来作为解决方案。
com.bumptech.glide.manager.Lifecycle
com.bumptech.glide.manager.LifecycleListener
不过 Android 官方推出 lifecycle 组件后,Glide 其实没必要使用自定义接口了。不过截至目前(2019.5.6)它仍然是使用 Lifecycle,也许是有历史包袱,也许是不想信赖 lifecycle 库。
Lifecycle 与可测试
将代码从 Activity/Fragment 抽出来封装成 lifecycle-aware 组件后带来的一个额外好处是可以单元测试。(Activity/Fragment 中的代码几乎没办法单元测试!)
1 | class MyLifeCycleObserverTest { |
Kotlin 代码中使用 mockito 时会遇到无法 mock final 类的问题,解决方法见这里。
完整的代码见 Github