从 API 1 开始,处理 Activity 的生命周期 (lifecycle) 就是个老大难的问题,基本上开发者们都看过这两张生命周期流程图:
随着 Fragment 的加入,这个问题也变得更加复杂:
而开发者们面对这个挑战,给出了非常稳健的解决方案: 分层架构。
分层架构
如上图所示,通过将应用分为三层,现在只有最上面的 Presentation 层 (以前叫 UI 层) 才知道生命周期的细节,而应用的其他部分则可以安全地忽略掉它。
而在 Presentation 层内部也有进一步的解决方案: 让一个对象可以在 Activity 和 Fragment 被销毁、重新创建时依然留存,这个对象就是架构组件的 ViewModel 类。下面让我们详细看看 ViewModel 工作的细节。
如上图,当一个视图 (View) 被创建,它有对应的 ViewModel 的引用地址 (注意 ViewModel 并没有 View 的引用地址)。ViewModel 会暴露出若干个 LiveData,视图会通过数据绑定或者手动订阅的方式来观察这些 LiveData。
当设备配置改变时 (比如屏幕发生旋转),之前的 View 被销毁,新的 View 被创建:
这时新的 View 会重新订阅 ViewModel 里的 LiveData,而 ViewModel 对这个变化的过程完全不知情。
归根到底,开发者在执行一个操作时,需要认真选择好这个操作的作用域 (scope)。这取决于这个操作具体是做什么,以及它的内容是否需要贯穿整个屏幕内容的生命周期。比如通过网络获取一些数据,或者是在绘图界面中计算一段曲线的控制锚点,可能所适用的作用域不同。如何取消该操作的时间太晚,可能会浪费很多额外的资源;而如果取消的太早,又会出现频繁重启操作的情况。
在实际应用中,以我们的 Android Dev Summit 应用为例,里面涉及到的作用域非常多。比如,我们这里有一个活动计划页面,里面包含多个 Fragment 实例,而与之对应的 ViewModel 的作用域就是计划页面。与之相类似的,日程和信息页面相关的 Fragment 以及 ViewModel 也是一样的作用域。
此外我们还有很多 Activity,而和它们相关的 ViewModel 的作用域就是这些 Activity。
您也可以自定义作用域。比如针对导航组件,您可以将作用域限制在登录流程或者结账流程中。我们甚至还有针对整个 Application 的作用域。
有如此多的操作会同时进行,我们需要有一个更好的方法来管理它们的取消操作。也就是 Kotlin 的协程 (Coroutine)。
协程的优势
协程的优点主要来自三个方面:
- 很容易离开主线程。我们试过很多方法来让操作远离主线程,AsyncTask、Loaders、ExecutorServices……甚至有开发者用到了 RxJava。但协程可以让开发者只需要一行代码就完成这个工作,而且没有累人的回调处理。
- 样板代码最少。协程完全活用了 Kotlin 语言的能力,包括 suspend 方法。编写协程的过程就和编写