Android MediatorLiveData

作者:louisgeek日期:2026/3/8

Android MediatorLiveData

  • 可以监听多个 LiveData 源,并且能动态添加和移除源

合并多个 LiveData 源

1public class CombineViewModel extends ViewModel {
2    private final MutableLiveData<One> _oneLiveData = new MutableLiveData<>();
3    public final LiveData<One> oneLiveData = _oneLiveData;
4
5    private final MutableLiveData<Two> _twoLiveData = new MutableLiveData<>();
6    public final LiveData<Two> twoLiveData = _twoLiveData;
7
8    //MediatorLiveData
9    private final MediatorLiveData<CombinedData> _combinedData = new MediatorLiveData<>();
10    public final LiveData<CombinedData> combinedData = _combinedData;
11
12    private boolean isOneReceived = false;
13    private boolean isTwoReceived = false;
14
15    //构造函数
16    public CombineViewModel() {
17        // 监听 One 数据源
18        _combinedData.addSource(_oneLiveData, one -> {
19            isOneReceived = true;
20            mergeData();
21        });
22
23        // 监听 Two 数据源
24        _combinedData.addSource(_twoLiveData, two -> {
25            isTwoReceived = true;
26            mergeData();
27        });
28    }
29
30    private void mergeData() {
31        // 仅当两个数据源都收到新更新时,才执行合并
32        if (isOneReceived && isTwoReceived) {
33            // 获取当前两个数据源的最新值(允许 null)
34            One currentOne = _oneLiveData.getValue();
35            Two currentTwo = _twoLiveData.getValue();
36            // 合并数据(即使有 null 也合并)
37            _combinedData.setValue(new CombinedData(currentOne, currentTwo));
38            // 重置标志位:保证每轮双源更新仅合并一次
39            isOneReceived = false;
40            isTwoReceived = false;
41        }
42    }
43
44    @Override
45    protected void onCleared() {
46        super.onCleared();
47        _combinedData.removeSource(_oneLiveData);
48        _combinedData.removeSource(_twoLiveData);
49        //
50        //isOneReceived = false;
51        //isTwoReceived = false;
52    }
53}
54

如果数据分布在不同 ViewModel 中

1public class CombineViewModel extends ViewModel {
2    //MediatorLiveData
3    private final MediatorLiveData<CombinedData> _combinedData = new MediatorLiveData<>();
4    public final LiveData<CombinedData> combinedData = _combinedData;
5    //
6    private OneViewModel oneViewModel;
7    private TwoViewModel twoViewModel;
8    private boolean isOneReceived = false;
9    private boolean isTwoReceived = false;
10
11    public void init(ViewModelProvider viewModelProvider) {
12        clearSources();
13
14        oneViewModel = viewModelProvider.get(OneViewModel.class);
15        twoViewModel = viewModelProvider.get(TwoViewModel.class);
16
17        //监听 One 数据源
18        _combinedData.addSource(oneViewModel.oneLiveData, one -> {
19            isOneReceived = true;
20            mergeData();
21        });
22
23        // 监听 Two 数据源
24        _combinedData.addSource(twoViewModel.twoLiveData, two -> {
25            isTwoReceived = true;
26            mergeData();
27        });
28    }
29
30    private void mergeData() {
31        if (isOneReceived && isTwoReceived) {
32            //双源都更新过才合并
33            One currentOne = oneViewModel.oneLiveData.getValue();
34            Two currentTwo = twoViewModel.twoLiveData.getValue();
35            // 更新合并后的数据(允许null)
36            _combinedData.setValue(new CombinedData(currentOne, currentTwo));
37            // 重置标志位
38            isOneReceived = false;
39            isTwoReceived = false;
40        }
41    }
42
43    //清理监听
44    public void clearSources() {
45        if (oneViewModel != null) {
46            _combinedData.removeSource(oneViewModel.oneLiveData);
47        }
48        if (twoViewModel != null) {
49            _combinedData.removeSource(twoViewModel.twoLiveData);
50        }
51        oneViewModel = null;
52        twoViewModel = null;
53        //
54        isOneReceived = false;
55        isTwoReceived = false;
56    }
57
58    @Override
59    protected void onCleared() {
60        super.onCleared();
61        //
62        clearSources();
63    }
64}
65

Transformations#map

  • 基于 MediatorLiveData
1private val _userLD = MutableLiveData<User>()
2val userLD: LiveData<User> = _userLD
3//更新用户信息
4fun updateUser(firstName: String, lastName: String, age: Int = 0) {
5    _userLD.value = User(firstName, lastName, age)
6}
7// User 转成 String
8val userFullNameLD: LiveData<String> = _userLD.map { user -> user.firstName + user.lastName }
9// Int 转成 String
10val ageTextLD: LiveData<String> = _userLD.map { user -> user.age.toString() }
11

感受一下源码

1//相当于 MediatorLiveData 收集(合并) 1  LiveData 
2//从 LiveData<X> 转成 LiveData<Y>,比如 LiveData<User> 转成 LiveData<String> 
3public fun <X, Y> LiveData<X>.map(mapFunction: Function<X, Y>): LiveData<Y> {
4    val combinedData = MediatorLiveData<Y>()
5    //监听 this 这个 LiveData 数据源
6    //转换好需要的值后再调用 combinedData#setValue
7    combinedData.addSource(this) { x -> combinedData.value = mapFunction.apply(x) }
8    return combinedData
9}
10

Transformations#switchMap

  • 基于 MediatorLiveData
1private val _userIdLD: MutableLiveData<String> = MutableLiveData() 
2val userIdLD: LiveData<String> = _userIdLD
3fun updateUserId(userId: String) {
4     _userIdLD.value = userId
5}
6//String  LiveData<User> (数据源动态切换)
7val userLD: LiveData<User> = _userIdLD.switchMap { userId -> userRepository.getUserByUserId(userId) }
8//
9

感受一下源码

1public fun <X, Y> LiveData<X>.switchMap(switchMapFunction: Function<X, LiveData<Y>>): LiveData<Y> {
2    val combinedData = MediatorLiveData<Y>()
3    //监听 this 这个 LiveData 数据源
4    combinedData.addSource(this, object : Observer<X> {
5            //主要是为了记录旧数据源,用作对比和移除操作
6            var liveData: LiveData<Y>? = null
7            override fun onChanged(value: X) {
8                //根据最新的 X 转成 LiveData(Y)
9                //比如 switchMapFunction.apply("002") 返回用户 userId=002  LiveData<User> 信息
10                val newLiveData = switchMapFunction.apply(value)
11                if (liveData === newLiveData) {
12                    //引用相等,说明 newLiveData 已经在被 combinedData 收集监听了,不需要继续 removeSource  addSource 重走一遍
13                    return
14                }
15                if (liveData != null) {
16                    //如果有旧的数据源,先移除监听,否则旧的数据还会被收集监听
17                    combinedData.removeSource(liveData!!)
18                }
19                liveData = newLiveData
20                if (liveData != null) {
21                    combinedData.addSource(liveData!!) { y -> combinedData.setValue(y) }
22                }
23            }
24        }
25    )
26    return combinedData
27}
28

ViewModel 复用 UseCase 的方法

1private final MutableLiveData<String> _liveDataSetConfig = new MutableLiveData<>();
2//<success,param>     回传 param 是为了方便监听的时候使用
3public final LiveData<Pair<Boolean, String>> liveDataSetConfig = Transformations.switchMap(_liveDataSetConfig, configUseCase::execute);
4public void setConfig(String param) {
5    _liveDataSetConfig.setValue(param);
6}
7
1//ConfigUseCase
2public LiveData<Pair<Boolean, String>> execute(String param) {
3        MutableLiveData<Pair<Boolean, String>> result = new MutableLiveData<>();
4        ThreadUtil.runXxx(() -> {
5            try {
6                //耗时操作
7                boolean success = Api.setConfig(param);
8                XLog.i(TAG + " setConfig success= " + success);
9                result.postValue(Pair.create(success, param));
10            } catch (Exception e) {
11                result.postValue(Pair.create(false, param));
12            }
13        });
14        return result;
15}
16

Android MediatorLiveData》 是转载文章,点击查看原文


相关推荐


【AI个人学习】npm本地安装claude code白嫖minimax模型
汐瀼2026/2/28

安装nodejs 下载 需要自取,下一步傻瓜式操作 通过网盘分享的文件:node-v24.13.0-x64.msi 链接: https://pan.baidu.com/s/1eJhCowFZ211oV2yxAfPvQA?pwd=sayg 提取码: sayg –来自百度网盘超级会员v7的分享 系统变量添加全局包路径 打开CMD敲命令 npm config get prefix # 获取npm全局包路径,获取后复制 添加路径到系统变量即可,添加系统变量网上教程一大堆 安装claude


你是不是觉得 R8 很讨厌,但 Android 为什么选择 R8 ?也许你对 R8 还不够了解
恋猫de小郭2026/2/20

本篇是来自 Android Developers 的播客 《What’s so great about R8?》 的整合,核心是讨论了 Android R8 编译器以及它对性能的影响,参与讨论的嘉宾包括来自 Android 工具团队、R8 团队和平台性能团队的专家(Tor Norby, Romain Guy, Sean, Chris, Shai)。 这是一篇让你对 R8 不再误解的内容。 D8 与 R8 编译器的区别 首先可能不少人还不理解 D8 与 R8 的区别,在 Android 开发里


如何零成本搭建个人站点
mCell2026/2/12

同步至个人站点:如何零成本搭建个人站点 站点地址:stack.mcell.top,包含完整的:写作、评论、部署、MCP支持... 我经常写作,最开始是在一些平台上,比如稀土掘金。后面慢慢写多了,就想有个自己的博客平台。 最初搭建的博客很简单:一个纯静态的 HTML 文件,内容也不复杂,写点自我介绍,当作个人站点。直接托管到 GitHub Pages,域名用的也是它默认那串。 但很快就发现:功能太少了。 比如发布文章?评论?甚至想加点扩展能力都很难——纯 HTML 又没框架,后面越改越痛苦。


PostgreSQL全文检索中文分词器配置与优化实践
MarsBighead2026/2/3

引言 在构建RAG(检索增强生成)系统的过程中,提升检索效率与准确性是一个持续优化的课题。除了常见的嵌入向量检索外,结合全文检索技术能进一步改善系统表现。本文基于PostgreSQL数据库,分享中文全文检索分词器的配置、索引创建与使用实践,记录在真实场景中遇到的问题与解决方案。 一、背景 为了提升RAG系统的检索效果,我们探索了全文检索与向量检索结合的混合检索方案。PostgreSQL内置了强大的全文检索功能,并支持扩展插件实现多语言分词。针对中文场景,我们选用了 zhparser 分词插件,


怎么理解 HttpServletRequest @Autowired注入
それども2026/1/24

在你的代码中,@Autowired(required = false) private HttpServletRequest req 的 required = false 是多余的,甚至可能带来潜在问题。以下是详细分析: 1. 为什么 required = false 是多余的? (1) HttpServletRequest 的特殊性 由 Servlet 容器(如 Tomcat)管理:HttpServletRequest 是 Web 请求的上下文对象,在 Servlet 环境中必然存在(


Objective-C 核心语法深度解析:基本类型、集合类与代码块实战指南
奋斗理想2026/1/16

详细讲解:Objective-C 基本类型、集合类和代码块 一、基本类型详解 1.1 主要基本类型 // MyTypes.m #import <Foundation/Foundation.h> void demonstrateBasicTypes() { NSLog(@"========== 基本类型演示 =========="); // 1. BOOL 类型(实际上是 signed char) BOOL isOpen = YES; // YES = 1,


SPI通信:从原理到工程实践
我太浮躁2026/1/8

文章目录 1、概述2、什么是SPI?2.1 SPI的特点是什么? 3、SPI的历史发展3.1 SPI诞生3.2 为什么是SPI?3.3 SPI的一路升级3.3.1 标准 SPI (Standard SPI)3.3.2 Dual SPI & Quad SPI (QSPI)3.3.3 Octal SPI (OSPI) / xSPI3.3.4 eSPI (Enhanced SPI) 4、协议架构及通信原理4.1 SPI功能层级划分(非官方,但实用便于理解)4.1.1 物理层 :四线制结


[服务器][教程]EC2开启自定义端口
踏雪Vernon2025/12/30

网上很多教程并没有说这一点。直接就说新建安全组之后就可以用了。 很坑,我一直以为我的服务器服务搭建的有问题。因为即使端口开了,端口没有对应的服务用端口扫描也是显示无连接的!! 1. 新建安全组规则 进入“实例”页面中找到“安全组”。新建一个安全组 新建之后如下所示。 新建之后,并不是直接就可以用了。而是要进行绑定!这个一定要注意!!! 2. 修改安全组规则 点击实例之后,选择“操作”,更改安全组 之后在这里选择刚才创建的安全组即可。其他的VPC应该也是类似的。 被网上的教程坑了。大家注意甄


【前端必看】手把手教你把 Strapi 5 自动化部署到宝塔,再也不用手动传代码了!
知航驿站2025/12/21

前言 兄弟们,作为一名普通前端,每次写完接口还要自己登录服务器、手动上传代码、装依赖、再重启 PM2,这一套“广播体操”做下来,天都黑了。 今天咱们就花 10 分钟,把这套活儿交给 GitHub Actions。以后你只管在本地 git push,剩下的脏活累活全让机器人帮你干! 在线文档 在线源码 一、 整体思路(大白话版) 代码放 GitHub:这大家都会。 GitHub Actions 开工:你一推代码,它就跳出来执行一个脚本。 SSH 远程登录:GitHub 像个“代跑腿”的,拿着你的


Webpack打包机制与Babel转译原理深度解析
老前端的功夫2025/12/13

Webpack打包机制与Babel转译原理深度解析 引言:现代前端构建工具的核心原理 Webpack和Babel是现代前端开发不可或缺的两个核心工具。Webpack解决了模块化、资源管理和打包优化的难题,而Babel则确保了JavaScript代码的浏览器兼容性。理解它们的底层原理不仅有助于更好地配置和使用这些工具,还能在遇到复杂问题时快速定位和解决。 一、Webpack打包机制深度解析 1.1 Webpack核心概念与架构设计 Webpack的整体架构: // Webpack 的核心抽象概念

首页编辑器站点地图

本站内容在 CC BY-SA 4.0 协议下发布

Copyright © 2026 XYZ博客