Jetpack介绍之MediatorLiveData

MediatorLiveData作为LiveData的子类,用于处理多个数据源的变化并整合数据。本文详细介绍了其使用方法,包括监听多个LiveData源的变化及限制数据变化次数等场景,并深入分析了源码实现,最后探讨了map()和switchMap()的使用。

1.介绍

MediatorLiveData是一个LiveData的子类,它将活跃状态或者非活跃状态传播到源LiveData上;也就是说它相当于是一个中间商,通过addSource进行注册的LiveData,当数据进行更新时通过中间商倒一手再进行处理。

2.具体使用

我们看一下官方介绍的两个场景:

  1. 有多个LiveData,我们想要同时监听这两个数据源,只要他们之中有一个数据源更新则接收到通知
    我们看一下具体需要怎么做:
//1.第一个数据源
LiveData liveData1 = ...;
//第二个数据源
LiveData liveData2 = ...;

//2.中间商
MediatorLiveData liveDataMerger = new MediatorLiveData<>();
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value)//数据源1更新后通知中间商更新);
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value)//数据源2更新后通知中间商更新);

//3.通过改变数据源1或者2的数据,中间商接收到更新了
liveDataMerger.observe(this, new Observer<String>() {
    @Override
    public void onChanged(String s) {
        Log.i("hellokai", "mediatorLiveData, onChanged:" + s);
    }
});
  1. 如果数据源变化频繁,我们想要只检测前10个数据变化,之后取消数据观察,我们可以用中间商这么做:
liveDataMerger.addSource(liveData1, new Observer() {
    private int count = 1;
    
    @Override public void onChanged(@Nullable Integer s) {
      count++;
      //1.当数据源1更新后,通知中间商进行更新
      liveDataMerger.setValue(s);
      //2.满足条件后,我们进行取消
      if (count > 10) {
          liveDataMerger.removeSource(liveData1);
      }
    }
});

可以看到我们没有影响原数据的注册和解注册,使用中间商倒一手可以灵活实现了!

3.源码分析

这个类总共不到200行代码,比较少,我们具体通过常用方法分析一下如何实现的

3.1来看一下addSource这个方法
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
    //创建了一个新的source对象看3.2
    Source<S> e = new Source<>(source, onChanged);
    Source<?> existing = mSources.putIfAbsent(source, e);
    if (existing != null && existing.mObserver != onChanged) {
        throw new IllegalArgumentException(
                "This source was already added with the different observer");
    }
    if (existing != null) {
        return;
    }
    //如果这个中间商之前通过数据更新,则在此进行注册,防止在onChanged数据解绑定再注册没有注册的问题
    if (hasActiveObservers()) {
        e.plug();
    }
}
3.2来看一下Source这个类
//继承自observer
private static class Source<V> implements Observer<V> {
    final LiveData<V> mLiveData;
    final Observer<? super V> mObserver;
    int mVersion = START_VERSION;

    Source(LiveData<V> liveData, final Observer<? super V> observer) {
        mLiveData = liveData;
        mObserver = observer;
    }

    void plug() {
        //mLiveData注册一直是active状态,直到调用removeObserver
        mLiveData.observeForever(this);
    }

    void unplug() {
        mLiveData.removeObserver(this);
    }

    @Override
    public void onChanged(@Nullable V v) {
        //将当前的mVersion和mLiveData.getVersion()进行绑定
        if (mVersion != mLiveData.getVersion()) {
            mVersion = mLiveData.getVersion();
            mObserver.onChanged(v);
        }
    }
}
3.3既然是LiveData的子类,再看一下onActive()onInactive()这两个方法
@CallSuper
@Override
protected void onActive() {
    for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
        source.getValue().plug();
    }
}

@CallSuper
@Override
protected void onInactive() {
    for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
        source.getValue().unplug();
    }
}

我们可以知道了mSources是在调用addSource()方法时绑定的数据源,当此中介者处于活跃状态时,进行注册和数据分发处理,如果是非活跃状态,也进行解注册。

4.Transformations使用分析

map()switchMap()方法使用到了MediatorLiveData这个类,使用同RxJava中的map()flatmap()方法使用是类似的;我们也来分析一下它的具体作用

public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
        @Override
        public void onChanged(@Nullable X x) {
            result.setValue(mapFunction.apply(x));
        }
    });
    return result;
}

可以看到,使用了中间商MediatorLiveData,绑定了源数据,当源数据改变的时候会用此Function来进行通知
我们再来看一下switchMap()这个方法:

public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
    final MediatorLiveData<Y> result = new MediatorLiveData<>();
    result.addSource(source, new Observer<X>() {
        LiveData<Y> mSource;

        @Override
        public void onChanged(@Nullable X x) {
            //1.获取到LiveData
            LiveData<Y> newLiveData = switchMapFunction.apply(x);
            //2.判断LiveData是否是新的,如果不是新的则返回
            if (mSource == newLiveData) {
                return;
            }
            //3.如果是新的,则先解注册
            if (mSource != null) {
                result.removeSource(mSource);
            }
            mSource = newLiveData;
            if (mSource != null) {
                //4.再将新的LiveData进行注册
                result.addSource(mSource, new Observer<Y>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}

那它的具体使用场景是什么呢?我们来看一下如果我们在ViewModel中直接返回了RepositoryLiveData那么因为Repository产生的LiveData每次返回的都是一个新的LiveData,这样如果不重新解注册之前的,重新注册新的就会有问题,这个时候我们就可以使用switchMap来做了,通过源码分析我们看到了,它会解注册之前的再注册新的LiveData

我们以官方文档中的例子再具体分析一下:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
       // 无论是通过网络retrofit还是走数据库都是返回的新的LiveData,直接返回的话,相当于每次都是重新注册的一个新的LiveData,没有必要
       return repository.getPostCode(address);
    }
}

可以使用switchMap()来做一个转换进行处理:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    //1.使用switchMap来保证只注册一个LiveData
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  //2.外部通过设置此方法来进行更新
  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

以上我们就清楚了它的使用场景了!

<think>我们正在讨论MediatorLiveData的定义和介绍。根据用户的问题和之前的对话历史,用户已经了解了MutableLiveData的注册回调顺序问题,现在转向MediatorLiveData。我们需要参考站内引用,特别是引用[1]、[2]、[3]和[4],来回答这个问题。 引用[1]提到:MediatorLiveData可以观察其他LiveData,当被观察的LiveData发生变化时,它会收到通知。 引用[2]提到:MediatorLiveData需要激活,激活机制与LifecycleObserver有关。 引用[3]提到:MediatorLiveData和MutableLiveData的用法,以及MediatorLiveData可以合并多个LiveData源,并且无论被观察的LiveData变化顺序如何,都能接收到最新的数据。 引用[4]展示了Transformations(包括map和switchMap)的使用,这些是建立在MediatorLiveData之上的工具。 因此,我们需要介绍MediatorLiveData的定义、作用、工作原理以及使用场景。 定义: MediatorLiveData是LiveData的子类,它可以观察多个LiveData源(包括MutableLiveData或其他MediatorLiveData),并将这些源的数据变化合并或转换为新的LiveData。换句话说,它是一个“中间人”LiveData,可以聚合多个数据源。 作用: 1. 合并多个LiveData源:例如,当界面需要同时依赖两个不同的数据源更新时,可以使用MediatorLiveData来组合这两个数据源,并在任一数据源变化时通知观察者。 2. 实现转换操作:Transformations.map和Transformations.switchMap内部就是使用MediatorLiveData来实现的,它们可以将一个LiveData的值转换为另一个形式。 工作原理: - MediatorLiveData通过addSource()方法添加对另一个LiveData的观察。当添加的LiveData数据变化时,MediatorLiveData的观察者也会被通知。 - 同时,MediatorLiveData可以移除对某个LiveData的观察(removeSource())。 - MediatorLiveData本身也是LiveData,因此具有生命周期感知能力,它会在活跃状态(STARTED或RESUMED)下才更新观察者。 使用场景(参考引用[3]): 假设有两个LiveData:LiveDataA和LiveDataB。我们创建一个MediatorLiveData,先后添加LiveDataA和LiveDataB为源。那么,无论LiveDataA和LiveDataB哪个先变化,MediatorLiveData都会将最新的数据(最后添加的源的最新数据)传递给观察者。注意:这里如果两个源都变化了,那么MediatorLiveData会收到多次通知,每次都会将当前最新的数据(可能是任意一个源)传递出去,但需要注意的是,它不会合并两个源的数据,而是每次只有一个源的数据被传递。如果需要合并,可以在onChanged回调中手动处理。 示例代码(参考引用[4]): 虽然引用[4]展示了Transformations,但我们可以给出一个简单的MediatorLiveData使用示例: ```java // 创建两个LiveData MutableLiveData<String> liveData1 = new MutableLiveData<>(); MutableLiveData<Integer> liveData2 = new MutableLiveData<>(); // 创建MediatorLiveData MediatorLiveData<String> mediatorLiveData = new MediatorLiveData<>(); // 添加源 mediatorLiveData.addSource(liveData1, value -> { // 当liveData1变化时,更新mediatorLiveData的值 mediatorLiveData.setValue("From liveData1: " + value); }); mediatorLiveData.addSource(liveData2, value -> { // 当liveData2变化时,更新mediatorLiveData的值 mediatorLiveData.setValue("From liveData2: " + value); }); // 观察mediatorLiveData mediatorLiveData.observe(this, value -> { // 这个观察者会在liveData1或liveData2变化时被调用 Log.d("Mediator", value); }); ``` 在上面的代码中,无论是liveData1还是liveData2发生变化,都会触发mediatorLiveData的观察者。 注意事项: - 当MediatorLiveData观察多个源时,需要小心避免重复设置相同的值(可能造成不必要的回调)或数据竞争。 - 如果某个源不再需要,应该调用removeSource()来移除,以避免内存泄漏或多余的回调。 总结: MediatorLiveData是一个功能强大的工具,用于处理多个LiveData源的复杂场景,它使得数据驱动的UI能够响应多个数据源的变化。 根据要求,生成相关问题: 1. MediatorLiveData如何避免多次回调? 2. MediatorLiveData与Transformations.map的关系是什么? 3. 在什么场景下应该使用MediatorLiveData而不是普通的MutableLiveData? 4. MediatorLiveData的addSource和removeSource方法的使用时机? 注意:在回答中引用站内引用时,在引用内容的末尾自然地添加引用标识,例如:[^1] </think>### MediatorLiveData 的定义与介绍 **MediatorLiveData** 是 Android Jetpack 架构组件中 `LiveData` 的子类,核心功能是**聚合多个 `LiveData` 源的数据变化**。它充当一个"中介观察者",可以将多个数据源统一管理,并在任一源数据变化时触发回调。以下是详细解析: --- #### 1. **核心定义与作用** - **数据聚合器**: MediatorLiveData 本身不直接持有数据,而是通过 `addSource()` 方法监听其他 `LiveData` 对象(如 `MutableLiveData` 或其他 `MediatorLiveData`)。当任一被观察的源数据变化时,它会自动将新值传递给自己的观察者[^1]。 - **生命周期感知**: 继承自 `LiveData`,具备完整的生命周期感知能力。仅在 `STARTED` 或 `RESUMED` 状态时激活观察逻辑,避免内存泄漏[^2]。 --- #### 2. **工作原理** - **注册数据源**: 使用 `addSource(sourceLiveData, observer)` 注册依赖项: ```kotlin val mediator = MediatorLiveData<String>() val sourceA = MutableLiveData<String>() val sourceB = MutableLiveData<Int>() // 添加源并定义转换逻辑 mediator.addSource(sourceA) { value -> mediator.value = "A: $value" // 将 sourceA 的值转换为新格式 } mediator.addSource(sourceB) { value -> mediator.value = "B: $value" // 将 sourceB 的值转换为新格式 } ``` - **数据分发**: 当 `sourceA` 或 `sourceB` 的值变化时,`mediator` 会立即触发其观察者的 `onChanged()` 回调[^3]。 - **动态管理源**: 可通过 `removeSource()` 移除不再需要的数据源,优化性能。 --- #### 3. **关键特性** - **多源合并**: 解决多个异步数据源(如网络请求、数据库查询)的结果合并问题。例如:同时监听用户位置和天气 API 数据,合并后更新 UI。 - **顺序无关性**: 无论被观察的 `LiveData` 何时触发变化,`MediatorLiveData` 总能捕获最新值(即使数据变化早于观察者注册)[^3]。 - **数据转换基础**: Jetpack 的 `Transformations.map()` 和 `Transformations.switchMap()` 内部依赖 `MediatorLiveData` 实现数据流转换[^4]。 --- #### 4. **典型使用场景** 1. **组合异构数据源** 例如:需同时显示数据库中的用户信息 + 网络请求的实时天气: ```kotlin val userLiveData: LiveData<User> = database.getUser() val weatherLiveData: LiveData<Weather> = api.fetchWeather() val profileData = MediatorLiveData<Pair<User, Weather?>>().apply { addSource(userLiveData) { user -> value = Pair(user, weatherLiveData.value) } addSource(weatherLiveData) { weather -> value = Pair(userLiveData.value, weather) } } ``` 2. **条件化数据流** 动态切换数据源(如根据登录状态选择本地缓存或网络数据): ```kotlin val dataSource = MediatorLiveData<Data>() fun switchSource(isLoggedIn: Boolean) { dataSource.removeSource(currentSource) currentSource = if (isLoggedIn) networkLiveData else cacheLiveData dataSource.addSource(currentSource) { dataSource.value = it } } ``` --- #### 5. **与 MutableLiveData 的区别** | **特性** | **MutableLiveData** | **MediatorLiveData** | |------------------------|--------------------------------------|------------------------------------| | **数据持有** | 直接存储数据 | 不存储数据,仅中转数据流 | | **观察对象** | 观察自身值的变化 | 观察其他 `LiveData` 的变化 | | **适用场景** | 单一数据源 | 多数据源聚合或复杂转换 | | **灵活性** | 低(仅 `setValue()`/`postValue()`) | 高(动态增删数据源) | > **总结**: > `MediatorLiveData` 是处理**多数据源依赖**和**数据流转换**的核心工具。它通过解耦数据生产与消费,简化了复杂数据流的编排,适合需要聚合或动态切换数据源的场景[^1][^3][^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值