Android官方架构组件LiveData: 观察者模式领域二三事

本文是 《Android Jetpack 官方架构组件》 系列文章, LiveData自己很简单,但其表明却正是 MVVM 模式最重要的思想,即 数据驱动视图(也有叫观察者模式、响应式等)——这也是摆脱 顺序性编程思惟 的重要一步。android

回顾LiveData:从处境尴尬到咸鱼翻身

咱们都知道Google在去年的 I/O 大会很是隆重地推出了一系列的 架构组件,本文的主角,LiveData 正是其中之一,和LifecycleViewModelRoom比较起来,LiveData能够说是最受关注的组件也不为过,遗憾的是,在发布的最初,关注点是由于它饱含争议,至关一部分的开发者认为——LiveData 实在太 鸡肋 了!git

2017年的 Android 技术领域,RxJava无疑是煊赫一时的名词之一,其 观察者模式链式调用 所表现出来的 API 优秀地设计,使得它位于不少 Android项目技术选型中的 第一序列github

这时 Google 隆重推出了具备相似功能的 LiveData (其本质就是观察者模式),能够说是有点初生牛犊不怕虎的感受,开发者们情不自禁将LiveDataRxJava 进行了对比,结论基本出奇的一致—— LiveData所提供的功能,RxJava彻底足以胜任,然后者却同时具备庞大的生态圈,这是LiveData短期内难以撼动(替代)的。面试

时至今日,LiveData的使用者愈来愈多,最主要的缘由固然和Google的强力支持不无关系,可是LiveData自己优秀的设计和轻量级也吸引了愈来愈多开发者的青睐。编程

如今咱们须要去了解它了,咱们都知道,LiveData 本质是 观察者模式 的体现,可关键的问题是:markdown

观察者模式究竟是啥?!

讨论这个问题以前,咱们先看看 LiveData 的用法,这实在没什么技术难度,好比,你能够这样实例化一个LiveData并使用它:网络

为了保证代码简洁可读,示例代码我使用了Kotlin

如你所见,LiveData实际上就像一个 容器, 本文中它存储了一个String类型的引用,每当这个容器内 String的数据发生变化,咱们都能在回调函数中进行对应的处理,好比 Toast架构

这彷佛和咱们平常用到的 Button 控件的 setOnClickListener() 很是类似,实际上点击事件的监听也正是 观察者模式 的一种体现,对于观察者来讲,它并不关心观察对象 数据是如何过来的,而只关心数据过来后 进行怎样的处理app

这也就是说,事件发射的上游接收事件的下游 互不干涉,大幅下降了互相持有的依赖关系所带来的强耦合性。框架

我依然坚持学习原理比学习如何应用的优先级更高,所以咱们先来一一探究LiveData自己设计中存在的那些闪光点背后的故事。

LiveData是如何避免内存泄漏的

咱们都知道,RxJava在使用过程当中,避免内存泄漏是一个不可忽视的问题,所以咱们通常须要借助三方库好比RxLifecycleAutoDispose来解决这个问题。

而反观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的活跃状态 的源码,咱们先跳过,稍后咱们会详细探讨它。

    1. 咱们继续回到上上一个源码片断的第三步中,对于一个可观察的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 ?(补充纠正,onStop()方法中值应该为 "onStop")

公布答案:

意外的是,livedata.observer()的本次观察并无观察到 onCreateonStoponDestroy 的数据变动。

为何会这样?

还记得上文提到过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 有关的内容,这些内容并不是是将 LiveDataRxJava 进行对比一决高下—— 例如,Google官方提供了 LiveDataRxJava 互相进行转换的工具类:

developer.android.com/reference/a…

值得玩味的是,官方的工具类中,LiveDataRxJava的转换方法,返回值并不是是一个Flowable,而是一个Publisher接口:

正如我在注释中标注的,这个工具方法返回的是一个接口,很大程度上限制了咱们对RxJava众多强大操做符的使用,这是不是来自Google的恶意

固然不是,对于这种行为,个人理解是Google对于LiveData自己严格的约束——它只应该用于进行数据的观察,而不是花哨的操做;转换为Flowable固然很是简单,可是这种行为是否属于LiveData自己职责的逾越,更准确来讲,是否属于没必要要的过分设计?这些是咱们须要去细细揣度的。

我无从验证个人理解是否正确,可是个人这个理由已经足够说服我本身,再往下已再也不是LiveData的范畴,关于这一点我将会专门起一篇文章去进行更深刻的探讨,欢迎关注。

--------------------------广告分割线------------------------------

系列文章

争取打造 Android Jetpack 讲解的最好的博客系列

Android Jetpack 实战篇


关于我

Hello,我是却把清梅嗅,若是您以为文章对您有价值,欢迎 ❤️,也欢迎关注个人我的博客或者Github

若是您以为文章还差了那么点东西,也请经过关注督促我写出更好的文章——万一哪天我进步了呢?

相关文章
相关标签/搜索