Android MediatorLiveData

Android MediatorLiveData

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

合并多个 LiveData 源

java 复制代码
public class CombineViewModel extends ViewModel {
    private final MutableLiveData<One> _oneLiveData = new MutableLiveData<>();
    public final LiveData<One> oneLiveData = _oneLiveData;

    private final MutableLiveData<Two> _twoLiveData = new MutableLiveData<>();
    public final LiveData<Two> twoLiveData = _twoLiveData;

    //MediatorLiveData
    private final MediatorLiveData<CombinedData> _combinedData = new MediatorLiveData<>();
    public final LiveData<CombinedData> combinedData = _combinedData;

    private boolean isOneReceived = false;
    private boolean isTwoReceived = false;

    //构造函数
    public CombineViewModel() {
        // 监听 One 数据源
        _combinedData.addSource(_oneLiveData, one -> {
            isOneReceived = true;
            mergeData();
        });

        // 监听 Two 数据源
        _combinedData.addSource(_twoLiveData, two -> {
            isTwoReceived = true;
            mergeData();
        });
    }

    private void mergeData() {
        // 仅当两个数据源都收到新更新时,才执行合并
        if (isOneReceived && isTwoReceived) {
            // 获取当前两个数据源的最新值(允许 null)
            One currentOne = _oneLiveData.getValue();
            Two currentTwo = _twoLiveData.getValue();
            // 合并数据(即使有 null 也合并)
            _combinedData.setValue(new CombinedData(currentOne, currentTwo));
            // 重置标志位:保证每轮双源更新仅合并一次
            isOneReceived = false;
            isTwoReceived = false;
        }
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        _combinedData.removeSource(_oneLiveData);
        _combinedData.removeSource(_twoLiveData);
        //
        //isOneReceived = false;
        //isTwoReceived = false;
    }
}

如果数据分布在不同 ViewModel 中

java 复制代码
public class CombineViewModel extends ViewModel {
    //MediatorLiveData
    private final MediatorLiveData<CombinedData> _combinedData = new MediatorLiveData<>();
    public final LiveData<CombinedData> combinedData = _combinedData;
    //
    private OneViewModel oneViewModel;
    private TwoViewModel twoViewModel;
    private boolean isOneReceived = false;
    private boolean isTwoReceived = false;

    public void init(ViewModelProvider viewModelProvider) {
        clearSources();

        oneViewModel = viewModelProvider.get(OneViewModel.class);
        twoViewModel = viewModelProvider.get(TwoViewModel.class);

        //监听 One 数据源
        _combinedData.addSource(oneViewModel.oneLiveData, one -> {
            isOneReceived = true;
            mergeData();
        });

        // 监听 Two 数据源
        _combinedData.addSource(twoViewModel.twoLiveData, two -> {
            isTwoReceived = true;
            mergeData();
        });
    }

    private void mergeData() {
        if (isOneReceived && isTwoReceived) {
            //双源都更新过才合并
            One currentOne = oneViewModel.oneLiveData.getValue();
            Two currentTwo = twoViewModel.twoLiveData.getValue();
            // 更新合并后的数据(允许null)
            _combinedData.setValue(new CombinedData(currentOne, currentTwo));
            // 重置标志位
            isOneReceived = false;
            isTwoReceived = false;
        }
    }

    //清理监听
    public void clearSources() {
        if (oneViewModel != null) {
            _combinedData.removeSource(oneViewModel.oneLiveData);
        }
        if (twoViewModel != null) {
            _combinedData.removeSource(twoViewModel.twoLiveData);
        }
        oneViewModel = null;
        twoViewModel = null;
        //
        isOneReceived = false;
        isTwoReceived = false;
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        //
        clearSources();
    }
}

Transformations#map

  • 基于 MediatorLiveData
kotlin 复制代码
private val _userLD = MutableLiveData<User>()
val userLD: LiveData<User> = _userLD
//更新用户信息
fun updateUser(firstName: String, lastName: String, age: Int = 0) {
    _userLD.value = User(firstName, lastName, age)
}
// User 转成 String
val userFullNameLD: LiveData<String> = _userLD.map { user -> user.firstName + user.lastName }
// Int 转成 String
val ageTextLD: LiveData<String> = _userLD.map { user -> user.age.toString() }

感受一下源码

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

Transformations#switchMap

  • 基于 MediatorLiveData
kotlin 复制代码
private val _userIdLD: MutableLiveData<String> = MutableLiveData() 
val userIdLD: LiveData<String> = _userIdLD
fun updateUserId(userId: String) {
     _userIdLD.value = userId
}
//String 转 LiveData<User> (数据源动态切换)
val userLD: LiveData<User> = _userIdLD.switchMap { userId -> userRepository.getUserByUserId(userId) }
//

感受一下源码

kotlin 复制代码
public fun <X, Y> LiveData<X>.switchMap(switchMapFunction: Function<X, LiveData<Y>>): LiveData<Y> {
    val combinedData = MediatorLiveData<Y>()
    //监听 this 这个 LiveData 数据源
    combinedData.addSource(this, object : Observer<X> {
            //主要是为了记录旧数据源,用作对比和移除操作
            var liveData: LiveData<Y>? = null
            override fun onChanged(value: X) {
                //根据最新的 X 转成 LiveData(Y)
                //比如 switchMapFunction.apply("002") 返回用户 userId=002 的 LiveData<User> 信息
                val newLiveData = switchMapFunction.apply(value)
                if (liveData === newLiveData) {
                    //引用相等,说明 newLiveData 已经在被 combinedData 收集监听了,不需要继续 removeSource 再 addSource 重走一遍
                    return
                }
                if (liveData != null) {
                    //如果有旧的数据源,先移除监听,否则旧的数据还会被收集监听
                    combinedData.removeSource(liveData!!)
                }
                liveData = newLiveData
                if (liveData != null) {
                    combinedData.addSource(liveData!!) { y -> combinedData.setValue(y) }
                }
            }
        }
    )
    return combinedData
}

ViewModel 复用 UseCase 的方法

java 复制代码
private final MutableLiveData<String> _liveDataSetConfig = new MutableLiveData<>();
//<success,param>     回传 param 是为了方便监听的时候使用
public final LiveData<Pair<Boolean, String>> liveDataSetConfig = Transformations.switchMap(_liveDataSetConfig, configUseCase::execute);
public void setConfig(String param) {
    _liveDataSetConfig.setValue(param);
}
java 复制代码
//ConfigUseCase
public LiveData<Pair<Boolean, String>> execute(String param) {
        MutableLiveData<Pair<Boolean, String>> result = new MutableLiveData<>();
        ThreadUtil.runXxx(() -> {
            try {
                //耗时操作
                boolean success = Api.setConfig(param);
                XLog.i(TAG + " setConfig success= " + success);
                result.postValue(Pair.create(success, param));
            } catch (Exception e) {
                result.postValue(Pair.create(false, param));
            }
        });
        return result;
}
相关推荐
锋风1 天前
远程服务器运行Android Studio开发aosp源码
android
测试工坊1 天前
Android UI 卡顿量化——用数据回答"到底有多卡"
android
alexhilton3 天前
端侧RAG实战指南
android·kotlin·android jetpack
二流小码农3 天前
鸿蒙开发:路由组件升级,支持页面一键创建
android·ios·harmonyos
xq95273 天前
Android 手游SDK组件化开发实战指南
android
煤球王子3 天前
学习记录:Android14中的WiFi-wpa_supplicant(1)
android
张小潇3 天前
AOSP15 Input专题InputDispatcher源码分析
android
TT_Close3 天前
【Flutter×鸿蒙】debug 包也要签名,这点和 Android 差远了
android·flutter·harmonyos
Kapaseker3 天前
2026年,我们还该不该学编程?
android·kotlin