LiveData
本身很简单,但其代表却正是 MVVM模式最重要的思想,即 数据驱动视图(也有叫观察者模式、响应式等)——这也是摆脱 顺序性编程思维的重要一步。
回顾LiveData:从处境尴尬到咸鱼翻身
我们都知道Google在去年的 I/O 大会非常隆重地推出了一系列的 架构组件,本文的主角,LiveData正是其中之一,和Lifecycle
、ViewModel
、Room
比较起来,LiveData
可以说是最受关注的组件也不为过,遗憾的是,在发布的最初,关注点是因为它饱含争议,相当一部分的开发者认为——LiveData
实在太 鸡肋了!
2017年的 Android
技术领域,RxJava
无疑是炙手可热的名词之一,其 观察者模式和 链式调用所表现出来的 API 优秀地设计,使得它位于很多 Android项目技术选型中的 第一序列。
这时 Google 隆重推出了具有类似功能的 LiveData
(其本质就是观察者模式),可以说是有点初生牛犊不怕虎的感觉,开发者们不由自主将LiveData
和 RxJava
进行了对比,结论基本出奇的一致—— LiveData
所提供的功能,RxJava
完全足以胜任,而后者却同时具有庞大的生态圈,这是LiveData
短时间内难以撼动(替代)的。
时至今日,LiveData
的使用者越来越多,最主要的原因当然和Google的强力支持不无关系,但是LiveData
本身优秀的设计和轻量级也吸引了越来越多开发者的青睐。
现在我们需要去了解它了,我们都知道,LiveData
本质是 观察者模式的体现,可关键的问题是:
观察者模式到底是啥?!
讨论这个问题之前,我们先看看 LiveData
的用法,这实在没什么技术难度,比如,你可以这样实例化一个LiveData
并使用它:
如你所见,LiveData
实际上就像一个 容器, 本文中它存储了一个String
类型的引用,每当这个容器内 String
的数据发生变化,我们都能在回调函数中进行对应的处理,比如 Toast。
这似乎和我们日常用到的 Button
控件的 setOnClickListener()
非常相似,实际上点击事件的监听也正是 观察者模式的一种体现,对于观察者来说,它并不关心观察对象 数据是如何过来的,而只关心数据过来后 进行怎样的处理。
这也就是说,事件发射的上游和 接收事件的下游互不干涉,大幅降低了互相持有的依赖关系所带来的强耦合性。
我依然坚持学习原理比学习如何应用的优先级更高,因此我们先来一一探究LiveData
本身设计中存在的那些闪光点背后的故事。
LiveData是如何避免内存泄漏的
我们都知道,RxJava
在使用过程中,避免内存泄漏是一个不可忽视的问题,因此我们一般需要借助三方库比如RxLifecycle
、AutoDispose
来解决这个问题。
而反观LiveData
,当它被我们的Activity
订阅观察,这之后Activity
如果finish()
掉,LiveData
本身会自动“清理”以避免内存泄漏。
这是一个非常好用的特性,它的实现原理非常简单,其本质就是利用了Jetpack 架构组件中的另外一个成员—— Lifecycle。
让我们来看看LiveData
被订阅时内部的代码:
源码中的逻辑非常复杂,我们只关注核心代码:
- 1.首先我们在调用
LiveData.observer()
方法时,传递的第一个参数Acitivity
实际被向上抽象成为了LifecycleOwner
,第二个参数Obserser
实际就是我们的观察后的回调。
这里我们需要注意的是,执行
LiveData.observer()
方法时 必须处于主线程,否则会因为断言失败而抛出异常。
- 2.方法内部实际上将我们传入的2个参数包装成了一个新的
LifecycleBoundObserver
对象,它实现了 Lifecycle组件中的LifecycleObserver
接口:
这里就解释了为什么LiveData
能够 自动解除订阅而避免内存泄漏了,因为它内部能够感应到Activity
或者Fragment
的生命周期。
这种设计非常巧妙——在我们初识 Lifecycle组件时,总是下意识认为它能够对大的对象进行有效生命周期的管理(比如 Presenter),实际上,这种生命周期的管理我们完全可以应用到各个功能的基础组件中,比如大到吃内存的 MediaPlayer(多媒体播放器)、绘制设计复杂的 自定义View,小到随处可见的LiveData
,都可以通过实现LifecycleObserver
接口达到 感应生命周期并内部释放重的资源的目的。
关于上述代码中注释了 更新LiveData的活跃状态的源码,我们先跳过,稍后我们会详细探讨它。
-
- 我们继续回到上上一个源码片段的第三步中,对于一个可观察的
LiveData
来讲,当然存在多个观察者同时订阅观察的情况,因此考虑到这一点,Google的工程师们为每一个LiveData
配置了一个Map
存储所有的观察者。
- 我们继续回到上上一个源码片段的第三步中,对于一个可观察的
-
4.到了这一步,我们将第2步包装生成的对象交给我们传入的
Activity
,让它在不同的生命周期事件中去逐一通知其所有的观察者,当然也包含了我们的LiveData
。
数据更新后如何通知到回调方法?
LiveData
原生的API提供了2种方式供开发者更新数据, 分别是 setValue()
和postValue()
,官方文档明确标明:setValue()
方法必须在 主线程进行调用,而postValue()
方法更适合在执行较重工作 子线程中进行调用(比如网络请求等)——在所有情况下,调用setValue()
或postValue()
都会 触发观察者并更新UI。
柿子挑软的捏,我们先看setValue()
方法的实现原理:
通过保留最终的核心代码,我们很清晰了解了setValue()
方法为什么能更新LiveData
的值,并且通知到回调函数中的代码去执行,比如更新UI。
但是我们知道,普遍情况下,Android不允许在子线程更新UI,但是postValue()
方法却可以在子线程更新LiveData()
的数据,并通知更新UI,这是如何实现的呢?
其实答案已经呼之欲出了,就是通过 Handler
:
现在你已经对LiveData
整体了一个基本的了解了,接下来让我们开始去探究更细节的闪光点。
看完源码,你告诉我才算入门?
LiveData
本身非常简单,毕竟它本身的源码一共也就500行左右,也许你要说 准备面试粗读一遍源码就够了,很遗憾,即使是粗读了源码,也很难说能够完全招架更深入的提问...
让我们来看一道题目:在下述Activity完整的生命周期中,Activity
一共观察到了几次数据的变更——即 一共打印了几条Log?
公布答案:
意外的是,livedata.observer()
的本次观察并没有观察到 onCreate、onStop和 onDestroy的数据变更。为什么会这样?
还记得上文提到过2次的 LiveData的活跃状态(Active)相关代码吗?实际上,LiveData
内部存储的每一个LifecycleBoundObserver
本身都有shouldBeActive
的状态:
现在我们明白了,原来并不是只要在onDestroy()
之前为LiveData
进行更新操作,LiveData
的观察者就能响应到对应的事件的。
虽然我们明白了这一点,但是如果更深入的思考,你会又多一个问题,那就是:
- 既然
LiveData
已经能够实现在onDestroy()
的生命周期时自动解除订阅,为什么还要多此一举设置一个Active
的状态呢?
仔细想想,其实也不难得到答案,Activity
并非只有onDestroy()
一种状态的,更多时候,新的Activity
运行在栈顶,旧的Activity
就会运行在 background
——这时旧的Activity
会执行对应的onPause()
和onStop()
方法,我们当然不会关心运行在后台的Activity
所观察的LiveData
对象(即使数据更新了,我们也无从进行对应UI的更新操作),因此LiveData
进入 **InActive(待定、非活跃)**状态,return
并且不去执行对应的回调方法,是 非常缜密的优秀设计。
当然,有同学提出,我如果希望这种情况下,Activity
在后台依然能够响应数据的变更,可不可以呢?当然可以,LiveData
此外还提供了observerForever()
方法,在这种情况下,它能够响应到任何生命周期中数据的变更事件:
除此之外,源码中处处都是优秀的细节,比如对于observe()
方法和observerForever()
方法对应生成的包装类,后者方法生成的是AlwaysActiveObserver
对象,统一抽象为ObserverWrapper
。
这种即使只有2种不同场景,也通过代码的设计,将公共业务进行向上抽离为抽象类的严谨,也非常值得我们学习。
小结,与更深入的思考
本来写了更多,篇幅所限,最终还是决定删除了相当一部分和 RxJava
有关的内容,这些内容并非是将 LiveData
和 RxJava
进行对比一决高下—— 例如,Google官方提供了 LiveData
和 RxJava
互相进行转换的工具类:
developer.android.com/reference/a…
值得玩味的是,官方的工具类中,LiveData
向RxJava
的转换方法,返回值并非是一个Flowable
,而是一个Publisher
接口:
正如我在注释中标注的,这个工具方法返回的是一个接口,很大程度上限制了我们对RxJava
众多强大操作符的使用,这是否是来自Google的恶意?
当然不是,对于这种行为,我的理解是Google对于LiveData
本身严格的约束——它只应该用于进行数据的观察,而不是花哨的操作;转换为Flowable当然非常简单,但是这种行为是否属于LiveData
本身职责的逾越,更准确来说,是否属于不必要的过度设计?这些是我们需要去细细揣度的。
我无从验证我的理解是否正确,但是我的这个理由已经足够说服我自己,再往下已不再是LiveData
的范畴,关于这一点我将会专门起一篇文章去进行更深入的探讨,欢迎关注。
作者:却把清梅嗅
链接:https://juejin.im/post/5c25753af265da61561f5335