Android LiveData组件详解以及LiveDataBus

article/2025/11/11 4:33:26

转载请标明出处:https://blog.csdn.net/zhaoyanjun6/article/details/99749323
本文出自【赵彦军的博客】

文章目录

  • LiveData简介
  • 使用
  • Java 使用方式
  • 两种订阅模式
    • observe
    • observeForever
  • 三、LiveDataBus
    • 3.1 为什么要用LiveDataBus替代EventBus和RxBus?
    • 3.2 LiveDataBus的设计和架构
    • 3.3 LiveDataBus原理图
    • 3.4 LiveDataBus的实现
      • 3.4.1 第一个实现
  • 参考资料

LiveData简介

LiveData 是一个可以被观察的数据持有类,它可以感知 Activity、Fragment或 Service 等组件的生命周期。简单来说,他主要有一下优点。

  • 它可以做到在组件处于激活状态的时候才会回调相应的方法,从而刷新相应的 UI,不用担心发生内存泄漏
  • 当 config 导致 activity 重新创建的时候,不需要手动取处理数据的储存和恢复。它已经帮我们封装好了。
  • 当 Actiivty 不是处于激活状态的时候,如果你想 livedata setValue 之后立即回调 obsever 的 onChange 方法,而不是等到 Activity 处于激活状态的时候才回调 obsever 的 onChange 方法,你可以使用 observeForever 方法,但是你必须在 onDestroy 的时候 removeObserver。

回想一下,在你的项目中,是不是经常会碰到这样的问题,当网络请求结果回来的时候,你经常需要判断 Activity 或者 Fragment 是否已经 Destroy, 如果不是 destroy,才更新 UI。而当你如果使用 Livedata 的话,因为它是在 Activity 处于 onStart 或者 onResume 的状态时,他才会进行相应的回调,因而可以很好得处理这个问题,不必谢一大堆的 activity.isDestroyed()。接下来,让我们一起来看一下 LiveData 的使用。

使用

LiveData 是一个抽象类,它的实现子类有 MutableLiveData MediatorLiveData。在实际使用中,用得比较多的MutableLiveData。他常常结合 ViewModel 一起使用。下面,让我们一起来看一下怎样使用它?

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.Observer
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.Button
import android.widget.TextViewclass MainActivity : AppCompatActivity() {lateinit var tv: TextViewvar liveData = MutableLiveData<String>() //定义liveDataprivate val changeObserver = Observer<String> { value ->value?.let { tv.text = it }}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)tv = findViewById(R.id.tv1)liveData.value = "123"liveData.observe(this, changeObserver)  //注册观察者findViewById<Button>(R.id.cancel).setOnClickListener {liveData.value = "456"}}
}

注意事项

必须要从主线程调用 setValue(T) 方法来更新 LiveData 对象;如果代码在工作线程中执行, 你可以使用 postValue(T) 方法来更新LiveData对象

Java 使用方式

MutableLiveData<String> mutableLiveData = new MutableLiveData<>();
//接受值
mutableLiveData.observe(MainActivity.this, new Observer<String>() {@Overridepublic void onChanged(@Nullable String s) {Log.d("yu--", " " + s);}});//发送值       mutableLiveData.setValue("abc" + System.currentTimeMillis());

两种订阅模式

observe

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {}

observe 需要传入 LifecycleOwner 对象,所以不需要我们取消订阅,有系统自动处理。

observeForever

public void observeForever(@NonNull Observer<T> observer) {}

使用 observeForever 注册的观察者接收器,需要我们在 页面销毁的时候取消注册。

取消注册也有两种方式

//取消单个Observer
public void removeObserver(@NonNull final Observer<T> observer) //取消 LifecycleOwner 下的所有 Observerpublic void removeObservers(@NonNull final LifecycleOwner owner) 

三、LiveDataBus

3.1 为什么要用LiveDataBus替代EventBus和RxBus?

  • LiveDataBus的实现及其简单 相对 EventBus 复杂的实现,LiveDataBus 只需要一个类就可以实现。
  • LiveDataBus可以减小APK包的大小 由于LiveDataBus只依赖 Android 官方 Android Architecture Components 组件的 LiveData ,没有其他依赖,本身实现只有一个类。作为比较,EventBus JAR包大小为57kb,RxBus依赖RxJava和RxAndroid,其中RxJava2 包大小 2.2 MB,RxJava1 包大小 1.1 MB,RxAndroid 包大小 9 kb。使用 LiveDataBus 可以大大减小 APK 包的大小。
  • LiveDataBus依赖方支持更好 LiveDataBus 只依赖 Android 官方 Android Architecture Components 组件的 LiveData ,相比 RxBus 依赖的 RxJava 和 RxAndroid,依赖方支持更好。
  • LiveDataBus具有生命周期感知 LiveDataBus 具有生命周期感知,在 Android 系统中使用调用者不需要调用反注册,相比 EventBus 和 RxBus 使用更为方便,并且没有内存泄漏风险。

3.2 LiveDataBus的设计和架构

LiveDataBus的组成

  • 消息 消息可以是任何的 Object,可以定义不同类型的消息,如 Boolean 、String 。也可以定义自定义类型的消息。
  • 消息通道 LiveData 扮演了消息通道的角色,不同的消息通道用不同的名字区分,名字是 String 类型的,可以通过名字获取到一个LiveData 消息通道。
  • 消息总线 消息总线通过单例实现,不同的消息通道存放在一个 HashMap 中。
  • 订阅 订阅者通过 getChannel 获取消息通道,然后调用 observe 订阅这个通道的消息。
  • 发布 发布者通过 getChannel 获取消息通道,然后调用 setValue 或者 postValue 发布消息。

3.3 LiveDataBus原理图

在这里插入图片描述

3.4 LiveDataBus的实现

3.4.1 第一个实现

public final class LiveDataBus {private final Map<String, MutableLiveData<Object>> bus;private LiveDataBus() {bus = new HashMap<>();}private static class SingletonHolder {private static final LiveDataBus DATA_BUS = new LiveDataBus();}public static LiveDataBus get() {return SingletonHolder.DATA_BUS;}public <T> MutableLiveData<T> getChannel(String target, Class<T> type) {if (!bus.containsKey(target)) {bus.put(target, new MutableLiveData<>());}return (MutableLiveData<T>) bus.get(target);}public MutableLiveData<Object> getChannel(String target) {return getChannel(target, Object.class);}
}

短短二十行代码,就实现了一个通信总线的全部功能,并且还具有生命周期感知功能,并且使用起来也及其简单:

注册订阅

LiveDataBus.get().getChannel("key_test", Boolean.class).observe(this, new Observer<Boolean>() {@Overridepublic void onChanged(@Nullable Boolean aBoolean) {}});

发送消息

LiveDataBus.get().getChannel("key_test").setValue(true);

我们发送了一个名为”key_test”,值为true的事件。

这个时候订阅者就会收到消息,并作相应的处理,非常简单。

问题出现
对于 LiveDataBus 的第一版实现,我们发现,在使用这个 LiveDataBus 的过程中,订阅者会收到订阅之前发布的消息。对于一个消息总线来说,这是不可接受的。无论 EventBus 或者 RxBus,订阅方都不会收到订阅之前发出的消息。对于一个消息总线, LiveDataBus 必须要解决这个问题。

问题原因总结
对于这个问题,总结一下发生的核心原因。对于 LiveData,其初始的 version是-1,当我们调用了其 setValue 或者 postValue ,其 vesion 会+1;对于每一个观察者的封装 ObserverWrapper,其初始 version 也为-1,也就是说,每一个新注册的观察者,其version 为-1;当LiveData设置这个 ObserverWrapper 的时候,如果 LiveData 的 version 大于 ObserverWrapper的version,LiveData 就会强制把当前 value 推送给 Observer。

如何解决这个问题
明白了问题产生的原因之后,我们来看看怎么才能解决这个问题。很显然,根据之前的分析,只需要在注册一个新的订阅者的时候把 Wrapper 的 version 设置成跟 LiveData 的 version 一致即可。

那么怎么实现呢,看看 LiveData的observe 方法,他会在步骤1创建一个 LifecycleBoundObserver,LifecycleBoundObserver 是ObserverWrapper 的派生类。然后会在步骤 2 把这个 LifecycleBoundObserver 放入一个私有 Map 容器 mObservers 中。无论ObserverWrapper 还是 LifecycleBoundObserver 都是私有的或者包可见的,所以无法通过继承的方式更改 LifecycleBoundObserver 的 version。

那么能不能从 Map 容器 mObservers 中取到 LifecycleBoundObserver ,然后再更改 version 呢?答案是肯定的,通过查看SafeIterableMap 的源码我们发现有一个 protected 的 get方法。因此,在调用 observe 的时候,我们可以通过反射拿到LifecycleBoundObserver,再把 LifecycleBoundObserver 的 version 设置成和 LiveData 一致即可。

LiveDataBus最终实现

public final class LiveDataBus {private final Map<String, BusMutableLiveData<Object>> bus;private LiveDataBus() {bus = new HashMap<>();}private static class SingletonHolder {private static final LiveDataBus DEFAULT_BUS = new LiveDataBus();}public static LiveDataBus get() {return SingletonHolder.DEFAULT_BUS;}public <T> MutableLiveData<T> with(String key, Class<T> type) {if (!bus.containsKey(key)) {bus.put(key, new BusMutableLiveData<>());}return (MutableLiveData<T>) bus.get(key);}public MutableLiveData<Object> with(String key) {return with(key, Object.class);}private static class ObserverWrapper<T> implements Observer<T> {private Observer<T> observer;public ObserverWrapper(Observer<T> observer) {this.observer = observer;}@Overridepublic void onChanged(@Nullable T t) {if (observer != null) {if (isCallOnObserve()) {return;}observer.onChanged(t);}}private boolean isCallOnObserve() {StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();if (stackTrace != null && stackTrace.length > 0) {for (StackTraceElement element : stackTrace) {if ("android.arch.lifecycle.LiveData".equals(element.getClassName()) &&"observeForever".equals(element.getMethodName())) {return true;}}}return false;}}private static class BusMutableLiveData<T> extends MutableLiveData<T> {private Map<Observer, Observer> observerMap = new HashMap<>();@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {super.observe(owner, observer);try {hook(observer);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void observeForever(@NonNull Observer<T> observer) {if (!observerMap.containsKey(observer)) {observerMap.put(observer, new ObserverWrapper(observer));}super.observeForever(observerMap.get(observer));}@Overridepublic void removeObserver(@NonNull Observer<T> observer) {Observer realObserver = null;if (observerMap.containsKey(observer)) {realObserver = observerMap.remove(observer);} else {realObserver = observer;}super.removeObserver(realObserver);}private void hook(@NonNull Observer<T> observer) throws Exception {//get wrapper's versionClass<LiveData> classLiveData = LiveData.class;Field fieldObservers = classLiveData.getDeclaredField("mObservers");fieldObservers.setAccessible(true);Object objectObservers = fieldObservers.get(this);Class<?> classObservers = objectObservers.getClass();Method methodGet = classObservers.getDeclaredMethod("get", Object.class);methodGet.setAccessible(true);Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);Object objectWrapper = null;if (objectWrapperEntry instanceof Map.Entry) {objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();}if (objectWrapper == null) {throw new NullPointerException("Wrapper can not be bull!");}Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");fieldLastVersion.setAccessible(true);//get livedata's versionField fieldVersion = classLiveData.getDeclaredField("mVersion");fieldVersion.setAccessible(true);Object objectVersion = fieldVersion.get(this);//set wrapper's versionfieldLastVersion.set(objectWrapper, objectVersion);}}
}

注册订阅

LiveDataBus.get().with("key_test", String.class).observe(this, new Observer<String>() {@Overridepublic void onChanged(@Nullable String s) {}});

发送消息

LiveDataBus.get().with("key_test").setValue(s);

源码说明
LiveDataBus 的源码可以直接拷贝使用,也可以前往作者的 GitHub 仓库查看下载:
https://github.com/JeremyLiao/LiveDataBus 。

参考资料

Android LiveData 使用详解
Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus


http://chatgpt.dhexx.cn/article/p4vC4ZhW.shtml

相关文章

【Android】MutableLiveData与LiveData

MutableLiveData笔记 MutableLiveData是什么&#xff1f;LiveData是什么&#xff1f;LiveData常用的方法MutableLiveData和LiveData的区别 关于postValue和setValue的机制简单理解 MutableLiveData是什么&#xff1f; public class MutableLiveData extends LiveData<T>…

Android架构——LifeCycle和LiveData原理学习总结

本文是楼主学习LifeCycle和LiveData原理的一些总结&#xff0c;本文不会长篇分析源码&#xff0c;而是利用类图和总结性的文字归纳原理。由于Livedata和LifeCycle有紧密联系&#xff0c;所以本文先总结LifeCycle原理&#xff0c;再总结LifeData原理。 本文LifeCycle基于版本an…

Android架构之LiveData组件

Jetpack组件系列文章 Android架构之LifeCycle组件 Android架构之Navigation组件(一) Android架构之Navigation组件(二) Android架构之Navigation组件(三) Android架构之Navigation组件(四) Android架构之ViewModel组件 Android架构之LiveData组件 Android架构之Room组件(一) An…

LiveData的基本使用

我们在《ViewModel的基本使用》这篇文章中提到了&#xff0c;ViewModel的主要作用是存放页面所需要的各种数据&#xff0c;而当这些数据发生变化时&#xff0c;我们采用接口的方式实现对页面的通知。 这样做是可行的&#xff0c;但如果要观察的数据很多&#xff0c;则需要定义大…

DataBinding与LiveData

DataBinding 一、添加配置二、使用修改布局文件具体使用单向绑定、方法绑定双向绑定、加载网络图片例子Adapter中使用使用 ObservableField 一、添加配置 如果需要使用databinding 需要在gradle中添加如下配置 defaultConfig {......//开启dataBindingdataBinding {enabled …

ViewModel+LiveData+DataBinding

1 ViewModel ViewModel要解决的问题 瞬态数据的丢失异步调用的内存泄露类膨胀提高维护难度和测试难度 ViewModel是介于视图和数据模型之间的桥梁&#xff0c;使数据和视图分离的同时还能通信。 2 LiveData ViewModel中可以存储普通数据&#xff0c;LiveDate数据 普通数据…

【Jetpack】LiveData 架构组件 ( LiveData 简介 | LiveData 使用方法 | ViewModel + LiveData 示例 )

文章目录 一、LiveData 简介二、LiveData 使用方法三、ViewModel LiveData 简单示例1、ViewModel LiveData 代码2、Activity 组件代码3、运行效果展示 四、ViewModel LiveData Fragment 通信示例1、ViewModel LiveData 代码2、Activity 组件代码Activity 代码布局文件 3、…

LiveData详细分析

目录介绍 01.LiveData是什么东西02.使用LiveData的优势03.使用LiveData的步骤04.简单使用LiveData05.observe()和observerForever()06.LiveData原理介绍07.observe订阅源码分析08.setValue发送源码分析09.observeForever源码10.LiveData源码总结 00.使用LiveData实现bus事件总…

LiveData+Room

文章目录 LiveData1.LiveData的作用2.LiveData的特点3.LiveData与ViewModel(无参数)的结合4.LiveData与ViewModel(有参数)的结合5.map()和switchMap()5.0 map()和switchMap()的作用5.1map()5.11没带ViewModel版5.12带ViewModel版 5.2switchMap() Room1.Room的定义2.Room的三个组…

Android架构组件(二)——LiveData

Android架构组件&#xff08;二&#xff09;——LiveData 上一篇文章讲到了Android架构组件之一Lifecycle组件&#xff08;Android 架构组件&#xff08;一&#xff09;——Lifecycle-Aware Components&#xff0c;现在我们再来看看另一个成员LiveData。 定义 简单地说&#xf…

Android LiveData 使用详解

说在前面 本次推出 Android Architecture Components 系列文章&#xff0c;目前写好了四篇&#xff0c;主要是关于 lifecycle&#xff0c;livedata 的使用和源码分析&#xff0c;其余的 Navigation&#xff0c; Paging library&#xff0c;Room&#xff0c;WorkMannager 等春节…

由浅入深,详解 LiveData 的那些事

引言 关于LiveData,在2022尾声的今天&#xff0c;从事 Android 开发的小伙伴一定不会陌生。相应的&#xff0c;关于 LiveData 解析与使用的文章更是数不胜数&#xff0c;其中不乏优秀的创作者&#xff0c;在众多的文章以及前辈面前&#xff0c;本篇也不敢妄谈能写的多么深入,易…

LiveData的原理和使用

LiveData Livedata是什么&#xff1f;它的作用是什么&#xff1f;我们能用它来干什么&#xff1f; 首先&#xff0c;LiveData是一种可观察的数据存储类。这句话可以看成两个部分&#xff0c;一个是可观察的类&#xff0c;另一个是数据存储的类。 LiveData 是可以被观察的&am…

python电子书合集

Python电子书合集 小编最早接触的编程语言是C&#xff0c;决心走上编程之路的是JavaScript&#xff0c;后来入职靠的Java&#xff0c;后来发现还有一门短小精悍的语言叫——Python。 本来以为是一门新语言&#xff0c;结果才发现是自己out了&#xff0c;没想到这门语言比小编还…

《Python Cookbook》(中文第三版):电子书

《Python Cookbook&#xff08;第3版&#xff09;中文版》介绍了Python应用在各个领域中的一些使用技巧和方法&#xff0c;其主题涵盖了数据结构和算法&#xff0c;字符串和文本&#xff0c;数字、日期和时间&#xff0c;迭代器和生成器&#xff0c;文件和I/O&#xff0c;数据编…

python编程入门电子书-《Python编程 从入门到实践》高清电子书免费下载

今天给大家分享一本书 获取方式 公众号后台回复 Python基础 获取百度网盘下载链接 书籍简介 本书旨在让你成为优秀的程序员&#xff0c;具体地说&#xff0c;是优秀的Python程序员。通过阅读本书&#xff0c;你将迅速掌握编程概念&#xff0c;打下坚实的基础&#xff0c;并…

【小沐学Python】Python实现在线电子书制作(MkDocs + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建新项目4、添加页面5、编辑导航页6、设置主题7、更改图标图标8、构建网站9、部署9.1 准备github项目9.2 注册登录Read the Docs9.3 导入github项目到 Read the Docs 10、Markdown语法10.1 横线10.2 标题10.3 段落10.4 文字高亮10.5 换行10.6 斜体…

超全的Python完全版电子书——从基础到爬虫、分析等高级应用,限时下载

python3.11即将于下半年发布&#xff0c;新的版本速度提升2倍&#xff0c;以弥补与其他编程语言在速度上的缺陷。可以预见Python语言在未来的应用范围会越来越广。 python学习方向建议&#xff1a; 如果你是本科及以下学历&#xff0c;建议你学习以下两个方向 1、爬虫。简单的…

【小沐学Python】Python实现在线电子书制作(Sphinx + readthedocs + github + Markdown)

文章目录 1、简介2、安装3、创建测试工程4、项目文件结构5、编译为本地文件6、编译为http服务7、更改样式主题8、支持markdown9、修改文档显示结构10、项目托管到github11、部署到ReadtheDocs结语 1、简介 Sphinx 是一个 文档生成器 &#xff0c;您也可以把它看成一种工具&…

python编程入门电子书-Python3零基础教材电子书合集

Python3零基础教材电子书合集&#xff0c;传送门&#xff1a;https://www.52pojie.cn/thread-676318-1-1.html 一、《Python编程从入门到实践》 链接&#xff1a;https://pan.baidu.com/s/1o9wJq0y 密码&#xff1a;12od 这书楼主现在也在看&#xff0c;讲的很细&#xff0c;…