前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >LiveData 非粘性消息的探索和尝试

LiveData 非粘性消息的探索和尝试

作者头像
GeeJoe
发布于 2021-12-09 04:18:46
发布于 2021-12-09 04:18:46
98100
代码可运行
举报
文章被收录于专栏:掘金文章掘金文章
运行总次数:0
代码可运行

LiveData 默认是支持粘性消息的(关于什么是粘性消息,请移步我的另一篇文章:LiveData 的正确使用姿势以及反模式 ),如何通过 LiveData 来实现非粘性消息呢,本文将在官博的基础上,分析几种尝试的方案,以及他们各自的优缺点

姿势一:重置 LiveData 的值

在 observer 里加上一个判断,当 LiveData 的值符合某个条件的时候,才做出响应的更新 UI 逻辑,然后提供一个重置 LiveData 值的方法,重置之后,observer 中条件判断为 fasle,因此可以达到不更新 UI 的目的

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
moneyReceivedViewModel.billLiveData.observe(this, Observer {
	if (it != null) {
		Toast.makeText(this, "到账$it元", Toast.LENGTH_SHORT).show()
	}
})
复制代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MoneyReceivedViewModel : ViewModel {
	private val _billLiveData = MutableLiveData<String>()
	val billLiveData: LiveData<String> = _billLiveData
	
	// 在 observe 之前和 show Toast 之后重置一下 LiveData
	fun reset() {
		_billLiveData.value = null
	}
}
复制代码

缺陷:

  1. 需要在 observer 中增加一些逻辑判断代码,这不符合简洁的 MVVM 模式(不应该在 View 层做过多的逻辑处理)
  2. 需要手动重置,不够优雅,一旦忘记重置就容易引发问题

姿势二:使用 SingleLiveEvent

SingleLiveEvent 是官方 sample 中封装的 LiveData,可以实现一个事件只被消费一,实现原理也很简单

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class SingleLiveEvent<T> : MutableLiveData<T>() {
    private val mPending: AtomicBoolean = AtomicBoolean(false)

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
        if (hasActiveObservers()) {
            Log.w(
                TAG,
                "Multiple observers registered but only one will be notified of changes."
            )
        }

        // Observe the internal MutableLiveData
        super.observe(owner, object : Observer<T?> {
            override fun onChanged(t: T?) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t)
                }
            }
        })
    }

    @MainThread
    override fun setValue(t: T?) {
        mPending.set(true)
        super.setValue(t)
    }

    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    fun call() {
        setValue(null)
    }

    companion object {
        private const val TAG = "SingleLiveEvent"
    }
}
复制代码

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MoneyReceivedViewModel : ViewModel() {
    val billLiveEvent = SingleLiveEvent<String>()

    fun payForLiveEvent(money: String) {
        billLiveEvent.value = money
    }
}
复制代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
viewModel.payForLiveEvent("100")

viewModel.billLiveEvent.observe(this, Observer {
    Log.d("sample", "到账 ${it} 元")
})

btn.setOnClickListener {
    viewModel.payForLiveEvent("200")
}

btn_wait.setOnClickListener {
    viewModel.billLiveEvent.observe(this, Observer {
        Log.d("sample", "到账 ${it} 元")
    })
}

// 以上代码在 Activity 的 onCreate() 中
// 当 Activity 启动的时候就会输出日志 -> 到账 100 元(没有实现不接收 observe() 之前的事件)
// 点击 btn 之后输出 -> 到账 200 元
// 点击 btn_wait 无输出(实现了事件只被消费一次)
复制代码

缺陷:

  1. 由于 setValue() 之后 onChange() 只会回调一次,因此,如果有多个 observer 的话,也只有一个才能收到回调,而且无法保证哪一个 observer 被回调(每个 observer 生命周期不一样,observe() 时机也不一样)
  2. 在 observe 之前发送的事件还是会被接收到,没有解决问题

姿势三:LiveData 包裹一个 Event

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
open class Event<out T>(private val content: T) {

    var hasBeenConsumed = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun consumed(): T? {
        return if (hasBeenConsumed) {
            null
        } else {
            hasBeenConsumed = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peek(): T = content
}
复制代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MoneyReceivedViewModel : ViewModel() {
    private val _billEvent = MutableLiveData<Event<String>>()

    val billEvent: LiveData<Event<String>>
        get() = _billEvent

    fun payForEvent(msg: String) {
        _billEvent.value = Event(msg)
    }
}
复制代码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
viewModel.payForEvent("100")

viewModel.billEvent.observe(this, Observer {
		it.consumed()?.let {
			Log.d("sample", "到账 ${it} 元")
		}
})

btn.setOnClickListener {
    viewModel.payForEvent("200")
}

btn_wait.setOnClickListener {
	  viewModel.billEvent.observe(this, Observer {
			it.consumed()?.let {
				Log.d("sample", "到账 ${it} 元")
			}
		})
}

// 以上代码在 Activity 的 onCreate() 中
// 当 Activity 启动的时候就会输出日志 -> 到账 100 元(没有实现不接收 observe() 之前的事件)
// 点击 btn 之后输出 -> 到账 200 元
// 点击 btn_wait 无输出(实现了事件只被消费一次)
复制代码

这种方式的好处是:

  • onChanged() 每次都会回调,但是是否要处理数据取决于 observer:consumed() 不返回已经被消费的消息,peek() 可返回已经被消费的数据

缺陷:

  1. 和姿势二一样,observe() 之前的数据还是会被监听到,没有解决问题
  2. 虽然可以添加多个 observers 且使用 peek() 来获取数据,但是还是无法实现多个 observer 都只接收一次事件

姿势四:支持多 observer 且仅接受 observe() 之后的消息

可参考 基于LiveData实现事件总线思路和方案

LiveData 并不是非用不可

我们使用了各种 workaround 的方式让 LiveData 支持粘性消息,以上几种方案也只有最后一种能够解决问题。但是笔者并不推荐使用这样的方式来绕过 LiveData 的限制,去打破 LiveData 原本的设计,这会让 LiveData 变得更让人难以理解

我们并不是非要用 LiveData 不可,LiveData 有适合自己的使用场景(具体可移步:LiveData 的正确使用姿势以及反模式 ),事件总线的场景已经有非常多的优秀开源库可以使用:EventBus、RxBus 等都可以供我们参考。

另一篇官博也提到了,如果我们项目中已经有一些比较成熟的方案,我们大可不必非要使用 LiveData

LiveData and RxJava Finally, let’s address the elephant in the room. LiveData was designed to allow the View observe the ViewModel. Definitely use it for this! Even if you already use Rx, you can communicate both with LiveDataReactiveStreams*. If you want to use LiveData beyond the presentation layer, you might find that MediatorLiveData does not have a toolkit to combine and operate on streams of data like RxJava offers. However, Rx comes with a steep learning curve. A combination of LiveData transformations (and Kotlin magic) might be enough for your case but if you (and your team) already invested in learning RxJava, you probably don’t need LiveData. *If you use auto-dispose, using LiveData for this would be redundant.

这里有些人可能会提到现有的 EventBus 或者 RxBus 等都没有生命周期感知能力,不能在生命周期销毁的时候自动解绑监听,而 LiveData 有这个能力,所以想使用 LiveData 来实现事件总线。这里其实我们可以换一种思路:给 callback 或者 EventBus 等增加 Lifecycle 感知能力,这样便能实现自动解绑了,这种方式相比使用 hack 的手段修改 LiveData 会更加友好一些,具体可以参考我的另一篇文章:自定义生命周期以及实现生命周期感知能力


相关文章

使用 Architecture Component 实现 MVVM 的正确姿势

LiveData 的正确使用姿势以及反模式

自定义生命周期以及实现生命周期感知能力

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年04月27日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
LiveData 的正确使用姿势以及反模式
借助 LiveData 的能力,在 View(Activity 或者 Fragment)和 ViewModel 之间,使用 LiveData 通信,当 LiveData 发生变化的时候,订阅了该 LiveData 的 View 能够收到通知以便做出相应的更新 UI 逻辑。
GeeJoe
2021/12/09
1.2K0
LiveData 的正确使用姿势以及反模式
LiveData的前世今生
这个系列我做了协程和Flow开发者的一系列文章的翻译,旨在了解当前协程、Flow、LiveData这样设计的原因,从设计者的角度,发现他们的问题,以及如何解决这些问题,pls enjoy it。
用户9239674
2021/12/01
1.1K0
使用 Architecture Component 实现 MVVM 的正确姿势
网上关于 MVVM 的介绍非常多,这里不再赘述,直接看一个例子,用直观的代码来感受一下用 MVVM 开发,是一种什么样的感受
GeeJoe
2021/12/09
7910
使用 Architecture Component 实现 MVVM 的正确姿势
Jetpack组件之LiveData
ViewModel 以注重生命周期的方式存储和管理界面相关的数据,当数据发生变化时,可通过接口的方式通知页面,但是有很多数据要通知时,需要定义大量的接口显得十分冗余,为此,Jetpack提供了LiveData组件解决这种问题,简化了开发过程。 LiveData 是一种可观察的数据存储器类。它是一个数据的容器,将数据包装起来,使数据成为被观察者,当数据发生变化时,观察者能够获得通知。 LiveData 具有生命周期感知能力,它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
八归少年
2022/06/29
8440
4. Jetpack源码解析—LiveData的使用及工作原理
2. Jetpack源码解析—Navigation为什么切换Fragment会重绘?
Hankkin
2019/07/10
1K0
Jetpack之LiveData详解
LiveData 是Jetpack中的一个组件,是一个可被观察的数据存储器类, 具有感知组件生命周期的能力,LiveData 可以感知组件生命周期活跃状态发送数据更新,在组件销毁时移除观察者对象,大多结合具有生命周期的组件一起使用(如 Activity、Fragment 或 Service,或实现了 LifecycleOwner 接口的对象)。
loongwind
2022/09/22
1.7K0
Jetpack之LiveData详解
LiveData详细分析
目录介绍 01.LiveData是什么东西 02.使用LiveData的优势 03.使用LiveData的步骤 04.简单使用LiveData 05.observe()和observerForever() 06.LiveData原理介绍 07.observe订阅源码分析 08.setValue发送源码分析 09.observeForever源码 10.LiveData源码总结 00.使用LiveData实现bus事件总线 利用LiveData实现事件总线,替代EventBus。充分利用了生命周期感知功能,可
杨充
2020/03/05
3K0
Android | LiveData 源码分析
LiveData 是一种持有可被观察的数据存储类,和其他可被观察的类不同的是,LiveData 是就要生命周期感知能力的,这意味着他可以在 Activity ,fragment 或者 service 生命周期活跃状态时 更新这些组件。
345
2022/09/27
1.1K0
Android Jetpack架构组件(四)之LiveData
LiveData是Jetpack架构组件Lifecycle 库的一部分,是一个可感知生命周期的可观察容器类 (Observable)。与常规的可观察类不同,LiveData 具有生命周期感知能力,这意味着它具有感知应用组件(如 Activity、Fragment 或 Service)的生命周期的能力,并且LiveData仅更新处于活跃生命周期状态的应用组件观察者。
xiangzhihong
2020/12/21
3.4K0
由浅入深,详解 LiveData 的那些事
关于LiveData,在2022尾声的今天,从事 Android 开发的小伙伴一定不会陌生。相应的,关于 LiveData 解析与使用的文章更是数不胜数,其中不乏优秀的创作者,在众多的文章以及前辈面前,本篇也不敢妄谈能写的多么深入,易懂。
Petterp
2022/12/07
1.4K0
由浅入深,详解 LiveData 的那些事
LiveData三问—阿里真题
主要思想就是用到了观察者模式思想,让观察者和被观察者解耦,同时还能感知到数据的变化,所以一般被用到ViewModel中,ViewModel负责触发数据的更新,更新会通知到LiveData,然后LiveData再通知活跃状态的观察者。
码上积木
2020/11/09
5010
AndroidJetpack Livedata应用场景分析
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力
用户9239674
2021/11/29
1.1K0
Android Jetpack - LiveData
LiveData 是一个可观察数据包装类,与普通观察者不同,LiveData 具备生命周期感知能力,这意味着它遵循其它应用组件的生命周期(Activity、Fragment、Service 等),此感知能力确保了 LiveData 只更新处于生命周期活跃状态的组件的观察者
SkyRiN
2019/08/12
2K0
年薪50万的Android岗,为什么连这7个Jetpack原理都答不上?
大家好,我是稳稳,一个曾经励志用技术改变世界,现在为随时失业做准备的中年奶爸程序员,与你分享生活和学习的点滴。
AntDream
2025/02/26
620
年薪50万的Android岗,为什么连这7个Jetpack原理都答不上?
Android Architecture Components Part2:LiveData
感谢你的再次光临,欢迎来到Android Architecture Components(AAC)系列文章。上篇文章我们一起讨论了Room,通过Room我们能够方便的操作App的数据库。如果你的App对本地数据库有所依赖的话,Room你值得拥有。
Rouse
2019/07/16
5620
Jetpack:在数据变化时如何优雅更新Views数据
三个步骤就定义了使用LiveData的方式,从步骤可以看出,使用了观察者模式,当LiveData对象持有数据发生变化,会通知对它订阅的所有处于活跃状态的订阅者。而这些订阅者通常是UI控制器,如Activity或Fragment,以能在被通知时,自动去更新Views。
Android技术干货分享
2019/07/02
3K0
Jetpack—LiveData组件的缺陷以及应对策略
为了解决Android-App开发以来一直存在的架构设计混乱的问题,谷歌推出了Jetpack-MVVM的全家桶解决方案。作为整个解决方案的核心-LiveData,以其生命周期安全,内存安全等优点,甚至有逐步取代EventBus,RxJava作为Android端状态分发组件的趋势。
2020labs小助手
2022/01/19
1.2K0
Android从零开始搭建MVVM架构(4)——LiveData
Livedata 是 Google 推荐的 Android 架构组件之一,是一个存放可被观察的数据持有类,有生命周期感知功能,解决了android开发者需要去手动处理生命周期的痛点。 比如当我们使用 Retrofit+Rxjava处理接口回调数据时,需要考虑activity 或 fragment 生命周期,以解决 onStop 或 onDestory之后回调数据的问题。现在只需要 Retrofit+Livedata 就好,其他的 Livedata 帮你做了。
老马的编程之旅
2022/06/22
2.4K0
【AAC 系列三】深入理解架构组件:LiveData
在之前我们深入研究了 Lifecycle 的实现原理,并在文末提到了LiveData 以及 ViewModel,这次我们来讲讲 LiveData。
程序亦非猿
2019/08/16
9270
【AAC 系列三】深入理解架构组件:LiveData
Android Jetpack系列 之LiveData
与普通可观察类不同的是LiveData具有生命周期感应能力,比如我们在页面中进行网络请求结束后,需要将数据显示在UI上,如果此时页面被销毁就会有空指针等异常,我们还需要在页面销毁的时候单独处理,而使用了LiveData之后就不需要我们手动的去处理这些了。
黄林晴
2020/05/25
1.1K0
Android Jetpack系列 之LiveData
相关推荐
LiveData 的正确使用姿势以及反模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文