您当前的位置: 首页 > 

Android技术栈

暂无认证

  • 0浏览

    0关注

    111博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

为何Google要将LiveData设计成粘性的?

Android技术栈 发布时间:2022-05-16 20:44:08 ,浏览量:0

前言

相信很多人在职业生涯的面试过程中都被问过一个问题?

面试官:我看你简历上有写 LiveData,那你能说说 LiveData 是粘性的吗?

这确实是一个值得深入思考的知识点,今天就让我们站在Google设计者的角度来深入学习一下LiveData

LiveData是粘性的吗?

关于这个问题,我们首先应该知道,粘性是什么意思?

不知道你对EventBus熟不熟悉,我第一次接触粘性这个概念,就来自于EventBus的粘性事件。

粘性事件:相对比普通事件,粘性事件支持先发送事件,再去注册订阅者。一旦完成订阅动作,这个订阅者就会接收到该粘性事件。

所以粘性其实就可以理解为观察者模式的升级,让观察者与被观察者对象之间更加的粘合。正常情况下,我们需要先注册观察者对象,然后再去更改被观察者对象,这样观察者对象才能接收到这个观察事件。而粘性,则支持先去触发更改被观察者对象,产生观察事件,然后再去注册观察者对象,注册成功后,该观察者对象还是可以接收到该观察事件,执行对应的观察动作。

本文对你有所帮助的话可以点赞支持一下喔, 需要更多android进阶开发资料可以留言获取~ 来个例子吧!看看是不是粘性的

我们利用LiveData来做APP的全局状态管理。

object GlobalState {

    val jcTestNumberLd: MutableLiveData = MutableLiveData()

}

然后在Fragment以及Activity中观察该jcTestNumberLd

/** 观察 GlobalState 的 Activity */
class JcTestActivity : BaseVmDbActivity() {

    override fun initView() {
        viewBinding.incButton.setOnClickListener {
            GlobalState.jcTestNumberLd.value =
                if (GlobalState.jcTestNumberLd.value == null) {
                    1
                } else {
                    GlobalState.jcTestNumberLd.value!!.toInt().inc()
                }
        }
    }

    override fun initObserve() {
        GlobalState.jcTestNumberLd.observe(this, {
            Log.e(TAG, "initObserve: jctestNumber = $it")
        })

    }

    ........
}

/** 观察 GlobalState 的 Fragment */
class EventFragment : BaseVmDbFragment() {

    override fun setObservers() {
        GlobalState.jcTestNumberLd.observe(viewLifecycleOwner, {
            Log.e(TAG, "setObservers: jctestNumber = $it", )
        })
    }

    ........
}

注意:这里例子中的 EventFragment 并不是关联到 JcTestActivity 的。用户会先进入到 JcTestActivity,然后由用户控制进入到另一个Activity中,加载EventFragment

我们来执行一下以下五步操作,来看一下输出的日志。

  1. 当我们第一次进入 JcTestActivity 时,注册了观察者,没有接收到观察事件,所以也就不会执行观察动作。
  2. 然后我们点击自增按钮为jcTestNumberLd 赋予新值,接收到观察事件,执行观察动作,输出 1
  3. 再次点击自增按钮,有观察事件,执行观察动作,输出 2
  4. 再次点击自增按钮,有观察事件,执行观察动作,输出 3
  5. 然后我们到 EventFragment 中,注册新的观察者,发现直接接收到观察事件,执行观察动作,输出 3

输出结果:

E/JcTestActivity: initObserve: jctestNumber = 1
E/JcTestActivity: initObserve: jctestNumber = 2
E/JcTestActivity: initObserve: jctestNumber = 3

E/EventFragment: setObservers: jctestNumber = 3
复制代码

这就是粘性事件!所以说,LiveData是粘性的。

LiveData 是怎么实现粘性的呢?

在知道LiveData是粘性后,我不经问自己:它是怎么实现粘性的呢?

这里我们先来回顾一下EventBus粘性事件的实现原理。

EventBus在发送粘性事件时,会将这粘性事件存到一个叫做stickyEvents的集合中,然后等注册订阅新的观察者对象时,会去遍历该集合中的粘性事件,如果有找到对应的粘性事件,就将该粘性事件发送给该观察者。

LiveData是不是也是以同样的原理来实现粘性的呢?

public LiveData(T value) {
    mData = value;
    mVersion = START_VERSION + 1;
}

/**
 * Creates a LiveData with no value assigned to it.
 */
public LiveData() {
    mData = NOT_SET;
    mVersion = START_VERSION;
}

LiveData 的构造函数中可以发现有一个 mVersion 参数,它代表着 LiveData 的版本号,每当我们进行setValue时,都会让mVersion进行自增。

另外,ObserverWrapper 这个观察者包装类中也有一个int mLastVersion = START_VERSION 版本号。

这两个版本号分别是被观察者对象与观察者对象的版本号,那这二者之间又有什么关系呢?

在判断是否通知观察者的 considerNotify(ObserverWrapper observer) 方法中,会对这两个版本号进行比较。

private void considerNotify(ObserverWrapper observer) {
    ...省略代码...

    //如果观者者的版本号 >= LiveData的版本号,就说明该观察者已经接收过该观察事件,也就不再分发。
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    //反之,分发观察事件给该观察者,让其执行对应的观察动作,并更新观察者的版本号
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);
}

概括一下:根据比对观察者对象的版本号与LiveData的版本号来判断是否分发当前版本的数据给该观察者。如果观察者对象的版本号大于等于LiveData的版本号,也就说明该观察者已经接收过当前版本的数据了,也就不需要再次分发了(等待下一次数据更新)。反之,则分发当前版本的数据给该观察者,让其执行对应的观察动作,并更新观察者的版本号,也就是更新为LiveData的版本号。

利用Hook验证一下

我们利用 Hook 分别拿到 LiveDatamVersion 以及 ObserverWrappermLastVersion 来看一下。

/** 主动 hook 检测版本号 */
dataBinding.hookCheckVersionButton.setOnClickListener {
    GlobalState.jcTestNumberLd.hook()
}

fun LiveData.hook() {
    //get liveData mVersion
    val mVersion = this.javaClass.superclass.getDeclaredField("mVersion")
    mVersion.isAccessible = true
    val mVersionValue = mVersion.get(this)
    Log.e(TAG, "hook: LiveData mVersion = $mVersionValue")

    val mObservers = this.javaClass.superclass.getDeclaredField("mObservers")
    mObservers.isAccessible = true
    //SafeIterableMap            
关注
打赏
1665219970
查看更多评论
0.0346s