漫谈MVVM(2)LiveData_LiveDataBus核心原理

val userLiveData = MutableLiveData()
userLiveData.postValue()// 入口代码

那么进入源码:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它实际上调用的是父类的postValue()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这段注释的意思大概是:

发送一个任务给主线程去设置给定的value。如果你在主线程中执行了如下代码:

liveData.postValue(“a”)

liveData.setValue(“b”)

这个value值 b 将会被首先设置,并且随后主线程将会用a覆盖它。

如果你在主线程执行post任务之前多次调用这个方法,那么只有最后一个value才会被分发。

接着往下看,方法内容:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里进行了一系列判定,规避了无需执行任务的情况.

ArchTaskExecutor是一个单例类,用来在主线程中执行任务,细节无需关心。

来看看 mPostValueRunnable

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个runnable,其实就做了两件事,1,传递刚刚更新的mPendingDatanewValue,然后还原mPendingData。2,将newValue值继续往下传输。

setValue做了什么

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

发现了重点,这个函数的注释上说明,如果存在存活状态的观察者,将会把这个value值分发给他们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

继续观察considerNotify()方法:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到,一个observer本身就有是否存活的状态值 mActive. 如果判定存活,就继续往下走程序,

这里我得到几个关键信息:

  • 观察者本身就有mActive属性

  • 为了防止观察者的mActive状态在通过了第一层判定之后突然改变(因为观察者的状态不可预知),又增加了第二层判定,如果这次判定发现观察者不再存活,也不通知

  • 观察者存在一个mLastVersion属性,影响是否会被通知到(这个有用,下一节LiveDataBus会讲),并且在正式通知观察者之后,mLastVersion会更新

  • 最终执行的是观察者的onChange方法,也就是我们在示例代码中所写的Observer<User>

user.userLiveData.observe(this, Observer {
Log.d(“hanTag”, “MainActivity:侦测到User发生变化$it”)
textView.text = it.toString()
})

接下来的重点就转移到了ObserverWrapper这个类,它是 LiveData的内部抽象类:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这个类,在我们去注册观察者的时候其实就用到了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那它作为一个观察者,他自己的存活状态mActive是由什么决定的呢?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

是ObserverWrapper自己的 activeStateChanged(). 这个函数的调用有4处,但是经过Debug追踪,最终锁定一处:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

LifecycleBoundObserver类的onStateChange函数中,这里已经说的很明白了,上面的代码解读一下:

  • mOwner 是 Activity或者Fragment,他们都是LifeCycleOwner接口的实现者
  • 判定观察者是否存活的依据,是 mOwner 是否处于至少START状态,但是排除DESTORYED状态
  • 如果mOwner的状态被判定为大于START 小于 DESTROYED, 那么观察者会被认定为存活,mActive被设置为true

OK.到此为止,作结论:

总结

LiveData之所以可以仅在观察者存活的时候通知到多个观察者,是因为:

  • 借助了LifeCycle,使用它来判定lifeCycleOwner的状态值,大于START小于DESTORY,则判定观察者是mActivetrue, 反之 false
  • LiveData数据发生变化时,轮询所有观察者,逐个判断观察者状态,发现mActivetrue,则通知,反之,则不通知

LiveDataBus

LiveDataBus基于LiveData, 是一种在application全局范围内的事件总线的新方案,当然这个并不是谷歌直接给的,在此之前,可能存在EventBusRxBus这种写法,但是他们都有内存泄漏的问题。而使用基于LiveDataLiveDataBus,将不会有类似的问题。

必须提到的是,原始的MutableLiveData存在多次通知的问题,解决方式也不止一种,后文再细讲。

核心功能

负责Activty/Fragment之间,多个Activity之间,以及FragmentActivity之间的数据通信。

基本用法

由于它不是谷歌提供的,是开发者根据需求创造的,所以这里我给出参考源码,实际开发中每个人的写法都可能不同:

package com.zhou.baselib.bus

import android.util.Log
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import java.util.concurrent.atomic.AtomicBoolean

class LiveDataBus {

/**

  • 只有当前存活的owner才能收到消息, 即,尚未创建成功的owner将不会收到消息 ,
  • 即使它进行了监听
    */
    private val mapOfAliveOwner: HashMap<String, AliveOwnerMutableLiveData<Any?>> = HashMap()

/**

  • 全部owner都能收到消息,就算将要跳转的Activity并没创建成功的情况
    */
    private val mapOfAllOwner: HashMap<String, MutableLiveData<Any?>> = HashMap()

/**

  • d
    */
    private val mapOfSingleEventOwner: HashMap<String, SingleLiveData<Any?>> = HashMap()

// 内部类的单例写法,静态成员天生就是线程安全的
class SingleHolder {
companion object {
val DATA_BUS = LiveDataBus()
}
}

companion object {
// 提供给外界一个get方法来获取单例对象
fun get(): LiveDataBus {
return SingleHolder.DATA_BUS
}
}

/**

  • 获取消息通道, 仅支持当前存活的 lifeCycleOwner
  • @key 消息通道的key
    */
    fun getAliveOwnerChannel(key: String): MutableLiveData<Any?> {
    if (!mapOfAliveOwner.containsKey(key)) {
    mapOfAliveOwner[key] = AliveOwnerMutableLiveData()
    }
    return mapOfAliveOwner[key]!!
    }

/**

  • 获取默认消息通道, 支持所有lifeCycleOwner,包括目前还没创建成功的
    */
    fun getDefaultChannel(key: String): MutableLiveData<Any?> {
    if (!mapOfAllOwner.containsKey(key)) {
    mapOfAllOwner[key] = MutableLiveData()
    }
    return mapOfAllOwner[key]!!
    }

fun getSingleEventChannel(key: String): SingleLiveData<Any?> {
if (!mapOfSingleEventOwner.containsKey(key)) {
mapOfSingleEventOwner[key] = SingleLiveData()
}
return mapOfSingleEventOwner[key]!!
}
}

/**

  • 只有当前存活的lifeCycleOwner才会收到 消息, 重写它的observer的mLastVersion
    */
    class AliveOwnerMutableLiveData : MutableLiveData() {

override fun observe(owner: LifecycleOwner, observer: Observer) {
super.observe(owner, observer)
hook(observer)
}

private fun hook(observer: Observer) {
val classLiveData: Class<LiveData<>> = LiveData::class.java
val fieldObservers = classLiveData.getDeclaredField(“mObservers”)
fieldObservers.isAccessible = true
val mObservers = fieldObservers[this]
val classObservers: Class<
> = mObservers.javaClass

val methodGet = classObservers.getDeclaredMethod(“get”, Any::class.java)
methodGet.isAccessible = true
val objectWrapperEntry = methodGet.invoke(mObservers, observer)
val objectWrapper =
(objectWrapperEntry as Map.Entry<*, >).value!!
val classObserverWrapper: Class<
>? = objectWrapper.javaClass.superclass

val fieldLastVersion =
classObserverWrapper!!.getDeclaredField(“mLastVersion”)
fieldLastVersion.isAccessible = true
val fieldVersion = classLiveData.getDeclaredField(“mVersion”)
fieldVersion.isAccessible = true
val objectVersion = fieldVersion[this]
fieldLastVersion[objectWrapper] = objectVersion
}

}

/**

  • 如果希望创建一个消息通道,只允许通知一次,那就使用SingleLiveEvent
  • @param the data type
    */
    class SingleLiveData : MutableLiveData() {
    private val mPending = AtomicBoolean(true)

override fun observe(owner: LifecycleOwner, observer: Observer) {
if (hasActiveObservers()) {
Log.d(
“SingleLiveEvent”,
“Multiple observers registered but only one will be notified of changes.”
)
}
// Observe the internal MutableLiveData
super.observe(owner, Observer { t ->
if (mPending.compareAndSet(true, false)) {// 获取bool值,并且在获取之后将它的值设置成false
observer.onChanged(t)
}
})
}

}

以Activity之间的通信为例:

  • 使用情形1,Activity之间的跳转,从MainActivity跳转到Main2Activity,要进行通信

// 第一个Activity
class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btn.setOnClickListener {
startActivity(Intent(this, Main2Activity::class.java))
// 获取通道,发送消息,通道的key是MainActivity
LiveDataBus.get().getDefaultChannel(“MainActivity2”).postValue(“发送给第二个Activity的消息”)
}
}
}

// 第二个Activity
class Main2Activity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)

// 监听事件通道
LiveDataBus.get().getDefaultChannel(“Main2Activity”).observe(this, Observer {
Log.d(“MainActivityTag”, “$it”)
})

}
}

运行程序,从MainActivity跳转到Main2Activity时,会看到日志:

Main2Activity收到消息: 发送给第二个Activity的消息

消息发送成功,这里的**postValue()**的参数类型,可以是任意对象,或者基础类型。

  • 使用情形2:以上的情况是从一个Activity,跳转到另一个Activity2,其实在发送消息之时,目标Activity2并没有创建成功,它也能在Activity2创建成功之后收到消息(下一节分析原理 )。但是也存在另一种情况,如果我们只希望目标Activity/Fragment存在的时候,才收到消息(而不是等他创建成功)。可以这么用。

// 第一个Activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

LiveDataBus.get().getAliveOwnerChannel(“MainActivity”).observe(this, Observer {
Log.d(“MainActivityTag”, “MainActivity 收到消息: $it”)
})

btn.setOnClickListener {
startActivity(Intent(this, Main2Activity::class.java))
LiveDataBus.get().getAliveOwnerChannel(“Main2Activity”).postValue(“发送给第二个Activity的消息”)
}
}
}

// 第二个Activity
class Main2Activity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main2)
LiveDataBus.get().getAliveOwnerChannel(“Main2Activity”).observe(this, Observer {
Log.d(“MainActivityTag”, “Main2Activity收到消息: $it”)
})
btn.setOnClickListener {
finish()
LiveDataBus.get().getAliveOwnerChannel(“MainActivity”).postValue(“回馈给Activity1的消息”)
}
}
}

当从MainActivty点按钮跳到Main2Activity时,Main2Activity并收不到消息。

这是因为,消息发送时,Main2Activity对象并不存在(如果Main2Activity已经处于栈内,并且它是栈内唯一的启动模式 SingleInstance,也能收到,亲测 )。

而从Main2Activity点按钮跳回MainActivity时, MainActivity能收到消息。

Fragment的情形也是一样,篇幅有限,这里就不做示例了…

  • 使用情形3

有的时候,当数据变化时,只需要通知一次View层即可。此时单独使用 SingleLiveData(参照LiveData基本用法),也可以使用LiveDataBus的getSingleEventChannel(“xxx”)取得 单次消息通道即可。

//注册监听
LiveDataBus.get().getSingleEventChannel(“NoticeTipsFragment”).observe(this, Observer {
tvMsg.text = it.toString()
})

// 发送消息
var seriCode = 1
tvMsg.setOnClickListener {//点击事件
LiveDataBus.get().getSingleEventChannel(“NoticeTipsFragment”).postValue(“哈哈哈${seriCode++}”)
}

就算点击多次按钮,发送了多次事件,监听器也只会收到一次通知。

核心原理

使用上述LiveDataBus类,只需要做两件事,

其一:获得消息通道 LiveDataBus.get().getXXXChannel(“xxxx”) 然后 observer(this,Observer{…})进行监听注册

其二:获得消息通道 LiveDataBus.get().getXXXChannel(“xxxx”) ** 然后 postValue(xxx) 发送消息

上面的LiveDataBus类提供了3种消息通道,

  • 一个是不支持粘性消息的getAliveOwnerChannel,内部使用的是hook之后的自定义AliveOwnerMutableLiveData对象来当作消息通道.

  • 一个是支持粘性消息的getDefaultChannel,使用的是纯天然的MutableLiveData

  • 还有一种是支持单次消息通知的 SingleEventLiveData, 也是重写了observer方法, 使用AtomicBoolean 的特性(compareAndSet(true,false)获取当前值,并且使用之后更改为false)完成单次通知。

LiveDataBus本身的设计,就是基于 订阅,发布者模式,和Rxjava如出一辙,但是由于利用到了LiveData,LifeCycle,它纯天然就带有生命周期感知机制,无需我们操心去做更多防止内存泄漏的工作。但是,LiveDataBusRxJava有一个共同之处,那就是支持粘性消息,即使 订阅者对象尚未创建,待到创建成功之后,也能收到。

进入源码, 本次探索源码,分析的目标是:

为何使用默认的MutableLiveData时,它支持粘性消息呢?

从发布者入手:

LiveDataBus.get().getDefaultChannel(“Main2Activity”).postValue(“发送给第二个Activity的消息”)

postValue之后,追踪到LiveData类的mPostValueRunnable

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

继续往下,进入到setValue

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

observer.mActive值是由 Activity/Fragment的状态决定的(前面LiveData的核心原理已经说明),

如果我想阻止消息的通知,想在这里执行return不现实。所以唯一的解决方案就锁定在 下图中的observer.mLastVersion >= mVersion 的判断上。只要我们能够让判断成立,那么这里就会return。onChange就不会执行,消息也就不会具备粘性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

一向严谨的谷歌工程师居然对这里的 mVersion变量加注释,但是根据代码逻辑可以判定:

只有在消息的版本mVersion 大于 订阅者的版本mLastVersion 时,才执行消息 通知.

这个也好理解,毕竟不能让订阅者收到过时的消息。

LiveData类的mVersion属性,唯一一处和当前情形有关的变动,就是:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

而它的初始值是 START_VERSION -1

observer.mLastVersion 的初始值也是 START_VERSION -1

总结

这次面试问的还是还是有难度的,要求当场写代码并且运行,也是很考察面试者写代码
因为Android知识体系比较庞大和复杂的,涉及到计算机知识领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
领域的方方面面。在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)
[外链图片转存中…(img-eZyCYcH4-1715339790101)]
里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值