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

栏目: IOS · Android · 发布时间: 5年前

内容简介:本文是本文默认读者已经学习了我们都知道Google在去年的 I/O 大会非常隆重地推出了一系列的

本文是 《Android Jetpack 官方架构组件》 系列的最后一篇文章,和一些朋友的观点不同的是,我认为它是 最重要 的核心组件,因为 LiveData 本身很简单,但其代表却正是 MVVM 模式最重要的思想,即 数据驱动视图 (也有叫观察者模式、响应式等)——这也是摆脱 顺序性编程思维 的重要一步。

本文默认读者已经学习了 Lifecycle , 欢迎关注笔者的Jetpack系列:

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

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

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

这时 Google 隆重推出了具有类似功能的 LiveData (其本质就是观察者模式),可以说是有点初生牛犊不怕虎的感觉,开发者们不由自主将 LiveDataRxJava 进行了对比,结论基本出奇的一致—— LiveData 所提供的功能, RxJava 完全足以胜任,而后者却同时具有庞大的生态圈,这是 LiveData 短时间内难以撼动(替代)的。

时至今日, LiveData 的使用者越来越多,最主要的原因当然和Google的强力支持不无关系,但是 LiveData 本身优秀的设计和轻量级也吸引了越来越多开发者的青睐。

现在我们需要去了解它了,我们都知道, LiveData 本质是 观察者模式 的体现,可关键的问题是:

观察者模式到底是啥?!

讨论这个问题之前,我们先看看 LiveData 的用法,这实在没什么技术难度,比如,你可以这样实例化一个 LiveData 并使用它:

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

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

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

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

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

LiveData是如何避免内存泄漏的

我们都知道, RxJava 在使用过程中, 避免内存泄漏 是一个不可忽视的问题,因此我们一般需要借助三方库比如 RxLifecycleAutoDispose 来解决这个问题。

而反观 LiveData ,当它被我们的 Activity 订阅观察,这之后 Activity 如果 finish() 掉, LiveData 本身会自动“清理”以避免内存泄漏。

这是一个非常好用的特性,它的实现原理非常简单,其本质就是利用了Jetpack 架构组件中的另外一个成员—— Lifecycle

让我们来看看 LiveData 被订阅时内部的代码:

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

源码中的逻辑非常复杂,我们只关注核心代码:

  • 1.首先我们在调用 LiveData.observer() 方法时,传递的第一个参数 Acitivity 实际被向上抽象成为了 LifecycleOwner ,第二个参数 Obserser 实际就是我们的观察后的回调。

这里我们需要注意的是,执行 LiveData.observer() 方法时 必须处于主线程 ,否则会因为断言失败而抛出异常。

  • 2.方法内部实际上将我们传入的2个参数包装成了一个新的 LifecycleBoundObserver 对象,它实现了 Lifecycle 组件中的 LifecycleObserver 接口:
Android官方架构组件LiveData: 观察者模式领域二三事

这里就解释了为什么 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() 方法的实现原理:

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

通过保留最终的核心代码,我们很清晰了解了 setValue() 方法为什么能更新 LiveData 的值,并且通知到回调函数中的代码去执行,比如更新UI。

但是我们知道, 普遍情况下,Android不允许在子线程更新UI ,但是 postValue() 方法却可以在子线程更新 LiveData() 的数据,并通知更新UI,这是如何实现的呢?

其实答案已经呼之欲出了,就是通过 Handler

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

现在你已经对 LiveData 整体了一个基本的了解了 ,接下来让我们开始去探究更细节的闪光点。

看完源码,你告诉我才算入门?

LiveData 本身非常简单,毕竟它本身的源码一共也就500行左右,也许你要说 准备面试粗读一遍源码就够了 ,很遗憾,即使是粗读了源码,也很难说能够完全招架更深入的提问...

让我们来看一道题目:在下述Activity完整的生命周期中, Activity 一共观察到了几次数据的变更——即 一共打印了几条Log ?(补充纠正,onStop()方法中值应该为 "onStop")

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

公布答案:

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

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

为什么会这样?

还记得上文提到过2次的 LiveData的活跃状态(Active) 相关代码吗?实际上, LiveData 内部存储的每一个 LifecycleBoundObserver 本身都有 shouldBeActive 的状态:

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

现在我们明白了,原来并不是只要在 onDestroy() 之前为 LiveData 进行更新操作, LiveData 的观察者就能响应到对应的事件的。

虽然我们明白了这一点,但是如果更深入的思考,你会又多一个问题,那就是:

  • 既然 LiveData 已经能够实现在 onDestroy() 的生命周期时自动解除订阅,为什么还要多此一举设置一个 Active 的状态呢?

仔细想想,其实也不难得到答案, Activity 并非只有 onDestroy() 一种状态的,更多时候,新的 Activity 运行在栈顶,旧的 Activity 就会运行在 background ——这时旧的 Activity 会执行对应的 onPause()onStop() 方法,我们当然不会关心运行在后台的 Activity 所观察的 LiveData 对象(即使数据更新了,我们也无从进行对应UI的更新操作),因此 LiveData 进入 **InActive(待定、非活跃)**状态, return 并且不去执行对应的回调方法,是 非常缜密的优秀设计

当然,有同学提出,我如果希望这种情况下, Activity 在后台依然能够响应数据的变更,可不可以呢?当然可以, LiveData 此外还提供了 observerForever() 方法,在这种情况下,它能够响应到任何生命周期中数据的变更事件:

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

除此之外,源码中处处都是优秀的细节,比如对于 observe() 方法和 observerForever() 方法对应生成的包装类,后者方法生成的是 AlwaysActiveObserver 对象,统一抽象为 ObserverWrapper

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

这种即使只有2种不同场景,也通过代码的设计,将公共业务进行向上抽离为抽象类的严谨,也非常值得我们学习。

小结,与更深入的思考

本来写了更多,篇幅所限,最终还是决定删除了相当一部分和 RxJava 有关的内容,这些内容并非是将 LiveDataRxJava 进行对比一决高下—— 例如,Google官方提供了 LiveDataRxJava 互相进行转换的 工具 类:

developer.android.com/reference/a…

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

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

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

当然不是,对于这种行为,我的理解是Google对于 LiveData 本身严格的约束——它只应该用于进行数据的观察,而不是花哨的操作;转换为Flowable当然非常简单,但是这种行为是否属于 LiveData 本身职责的逾越,更准确来说,是否属于 不必要的过度设计 ?这些是我们需要去细细揣度的。

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

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

关于我

Hello,我是 却把清梅嗅 ,如果您觉得文章对您有价值,欢迎 :heart:,也欢迎关注我的博客或者 Github

如果您觉得文章还差了那么点东西,也请通过 关注 督促我写出更好的文章——万一哪天我进步了呢?


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Python网络编程基础

Python网络编程基础

John Goerzen / 莫迟 等 / 电子工业出版社 / 2007 / 68.00元

《Python网络编程基础》可以作为各层次Python、Web和网络程序的开发人员的参考书,在实际工作中使用书中的技术,效果更佳。一起来看看 《Python网络编程基础》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换