【译】LiveData 使用详解

前言

本文翻译自【Understanding LiveData made simple】,详细介绍了 liveData 的使用。感谢作者 Elye。水平有限,欢迎指正讨论。 Architecture Components 可以说是 Google 提供给 Android 开发者的一大福利。LiveData 是其中的一个主要组件,下面我们一起看下该怎么使用好 LiveData。 如果你之前没了解过 Architecture Components,可以看下作者的另一篇文章:Android Architecture Components for Dummies in Kotlin (50 lines of code)。 在上一篇文章中,作者提到了 ViewModelLiveData,其中 LiveData 是用来从 ViewModel 层向 View 层传递数据。但当时并没有完整地介绍 LiveData 的作用,现在我们来详细看下 LiveData 的定义和使用。 那么,LiveData 有什么特别的地方呢?

正文

什么是 LiveData

官方 定义是:

LiveData 是一个可被观察的数据持有类。与普通的被观察者(如 RxJava 中的 Observable)不同的是,LiveData 是生命周期感知的,也就是说,它能感知其它应用组件(Activity,Fragment,Service)的生命周期。这种感知能力可以确保只有处于 active 状态的组件才能收到 LiveData 的更新。详情可查看 Lifecycle

这就是官方对 LiveData 的定义。 为了简单起见,我们先来看一些之前的开发习惯,以便更好地理解。

起源

当 Android 刚诞生的时候,大多数开发者写的代码都放在一个 Activity 中。

然而,把所有逻辑都放在一个 Activity 类中并不理想。因为 Activity 很难进行单元测试。 鉴于此,业界出现了MVC、MVP、MVVM 等开发架构,通过 Controller、Presenter、ViewModel 等分层抽离 Activity 中的代码。

这种架构能把逻辑从 View 层分离出来。然而,它的问题是 Controller、Presenter、ViewModel 等不能感知 Activity 的生命周期,Activity 的生命周期必须通知这些组件。 为了统一解决方案,Google 开始重视这个问题,于是 Architecture Components 诞生了。 其中 ViewModel 组件有一个特殊能力,我们不需要手动通知它们,就可以感知 Activity 的生命周期。这是系统内部帮我们做的事情。

除了 ViewModel 外,用于从 ViewModel 层暴露到 View 层的数据,也有生命周期感知的能力,这就是为什么叫做 LiveData 的原因。作为一个被观察者,它可以感知观察它的 Activity 的生命周期。

举例说明

为了更好地理解,下图将 LiveData 作为数据中心:

从上图可以看到,LiveData 的数据来源一般是 ViewModel,或者其它用来更新 LiveData 的组件。一旦数据更新后,LiveData 就会通知它的所有观察者,例如 Activity、Fragment、Service 等组件。但是,与其他类似 RxJava 的方法不同的是,LiveData 并不是盲目的通知所有观察者,而是首先检查它们的实时状态。LiveData 只会通知处于 Actie 的观察者,如果一个观察者处于 PausedDestroyed 状态,它将不会受到通知。 这样的好处是,我们不需要在 onPauseonDestroy 方法中解除对 LiveData 的订阅/观察。此外,一旦观察者重新恢复 Resumed 状态,它将会重新收到 LiveData 的最新数据。

LiveData 的子类

LiveData 是一个抽象类,我们不能直接使用。幸运的是,Google 提供了一些其简单实现,让我们来使用。

MutableLiveData

MutableLiveData 是 LiveData 的一个最简单实现,它可以接收数据更新并通知观察者。 例如:

// Declaring it
val liveDataA = MutableLiveData<String>()

// Trigger the value change
liveDataA.value = someValue

// Optionally, one could use liveDataA.postValue(value)
// to get it set on the UI thread
复制代码

观察 LiveData 也很简单,下面展示了在 Fragment 中订阅 LiveDataA

class MutableLiveDataFragment : Fragment() {

    private val changeObserver = Observer<String> { value ->
        value?.let { txt_fragment.text = it }
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        getLiveDataA().observe(this, changeObserver)
    }
    // .. some other Fragment specific code ..
}
复制代码

结果如下,一旦 LiveDataA 数据发生变化(例如7567和6269),Fragment 就会收到更新。

上面的代码中有这么一行:

getLiveDataA().observe(this, changeObserver)
复制代码

这就是订阅 LiveData 的地方,但是并没有在 Fragment pausingterminating 时解除订阅。 即使我们没有解除订阅,也不会有什么问题。看下面的例子,当 Fragment 销毁时,LiveData 不会因为产生一个新数据(1428)通知给 inactive 的 Fragment 而崩溃(Crash)。

同时,也可以看到当 Fragment 重新 active 时,将会收到最新的 LiveData 数据:1428。

Transformations#map()

我们一般定义一个 Repository 负责从网络或数据库获取数据,在将这些数据传递到 View 层之前,可能需要做一些处理。 如下图,我们使用 LiveData 在各个层之间传递数据:

我们可以使用 Transformations#map() 方法将数据从一个 LiveData 传递到另一个 LiveData。

class TransformationMapFragment : Fragment() {

    private val changeObserver = Observer<String> { value ->
        value?.let { txt_fragment.text = it }
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        val transformedLiveData = Transformations.map(
                getLiveDataA()) { "A:$it" }
        transformedLiveData.observe(this, changeObserver)
    }
    // .. some other Fragment specific code ..
}
复制代码

结果如下所示,以上代码将 LiveDataA 的数据(5116)进行处理后变为 A:5116

使用 Transformations#map() 有助于确保 LiveData 的数据不会传递给处于 dead 状态的 ViewModel 和 View。

这很酷,我们不用担心解除订阅。 下面来看下 Transformations#map() 的源码:

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

这里用到了 LiveData 的另一个子类 MediatorLiveData。接下来看一看这是个什么东西。

MediatorLiveData

Transformations#map() 源码中可以看到,MediatorLiveData 有一个 MediatorLiveData#addSource() 方法,这个方法改变了数据内容。 也就是说,我们可以通过 MediatorLiveData 将多个 LiveData 源数据集合起来,如下图所示:

代码如下:

class MediatorLiveDataFragment : Fragment() {

    private val changeObserver = Observer<String> { value ->
        value?.let { txt_fragment.text = it }
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        val mediatorLiveData = MediatorLiveData<String>()
        mediatorLiveData.addSource(getliveDataA())
              { mediatorLiveData.value = "A:$it" }
        mediatorLiveData.addSource(getliveDataB())
              { mediatorLiveData.value = "B:$it" }
        mediatorLiveData.observe(this, changeObserver)
    }
    // .. some other Fragment specific code ..
}
复制代码

这样,Fragment 就可以同时接收到 LiveDataALiveDataB 的数据变化,如下图所示:

有一点需要注意的是:当 Fragment 再处于 active 状态时,如果 LiveDataALiveDataB 的数据都发生了变化,那么当 Fragment 重新恢复 active 状态时,MediatorLiveData 将获取最后添加的 LiveData 的数据发送给 Fragment,这里即 LiveDataB

从上图可以看到,当 Fragment 恢复活动状态时,它就会收到 LiveDataB 的最新数据,无论 LiveDataB 变化的比 LiveDataA 变化的早或晚。从上面代码可以看到,这是因为 LiveDataB 是最后被添加到 MediatorLiveData 中的。

Transformations#switchMap

上面的示例中展示了我们可以同时监听两个 LiveData 的数据变化,这是很有用的。但是,如果我们想要手动控制只监听其中一个的数据变化,并能根据需要随时切换,这时应怎么办呢? 答案是:Transformations#switchMap(),Google 已经为我们提供了这个方法。它的定义如下:

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

        @Override
        public void onChanged(@Nullable X x) {
            LiveData<Y> newLiveData = func.apply(x);
            if (mSource == newLiveData) {
                return;
            }
            if (mSource != null) {
                result.removeSource(mSource);
            }
            mSource = newLiveData;
            if (mSource != null) {
                result.addSource(mSource, new Observer<Y>() {
                    @Override
                    public void onChanged(@Nullable Y y) {
                        result.setValue(y);
                    }
                });
            }
        }
    });
    return result;
}
复制代码

这个方法用来添加一个新数据源并相应地删除前一个数据源。因此 MediatorLiveData 只会包含一个 LiveData 数据源。这个控制开关也是一个 LiveData。整个过程如下所示:

使用方法如下:

class TransformationSwitchMapFragment : Fragment() {

    private val changeObserver = Observer<String> { value ->
        value?.let { txt_fragment.text = it }
    }

    override fun onAttach(context: Context?) {
        super.onAttach(context)

        val transformSwitchedLiveData =
            Transformations.switchMap(getLiveDataSwitch()) {
                switchToB ->
                if (switchToB) {
                     getLiveDataB()
                } else {
                     getLiveDataA()
                }
        }

        transformSwitchedLiveData.observe(this, changeObserver)
    }
    // .. some other Fragment specific code ..
}
复制代码

这样,我们就能很容易地控制用哪个数据来更新 View 视图,如下所示,当正在观察的 LiveData 发生变化,或者切换观察的 LiveData 时,Fragment 都会收到数据更新。

一个实际的使用场景是,我们可以通过特定设置(如用户登录 session)的不同数据源,来处理不同的业务逻辑。

源码地址

以上示例代码可以在作者的 Github 上找到:github.com/elye/demo_a…。 下载源码查看,能更好地理解。

更多示例

如果订阅了一个 LiveData,但又不想收到数据更新的通知,可以参考一下文章: LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)

参考

联系

我是 xiaobailong24,您可以通过以下平台找到我:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin LiveData 是用于 Android 开发的一个特性,它是一个发布-订阅模式的数据源。LiveData 允许你创建可以被观察(即订阅)的数据对象,从而让你的组件知道数据何时发生变化。这使得数据更新更加透明,并且可以轻松地将数据传递给依赖它的组件。 使用 Kotlin LiveData 的基本步骤如下: 1. 创建一个 LiveData 对象:LiveData 对象是你想要发布的数据的公共视图。你可以将任何可变的、可观察的数据(如变量、属性或对象)封装在 LiveData 对象中。 ```kotlin val liveData = MutableLiveData<Int>() ``` 2. 将数据设置为 LiveData 对象:你可以通过调用 LiveData 对象的 set 方法将数据发布出去。 ```kotlin liveData.value = 42 ``` 3. 将 LiveData 对象传递给依赖它的组件:你可以将 LiveData 对象传递给任何需要它的组件,如 ViewModel 或 Activity/Fragment。这样,当 LiveData 中的数据发生变化时,所有订阅该数据的组件都会收到通知。 4. 使用观察者更新 UI:一旦你订阅了 LiveData 对象,你就可以在 UI使用观察者来更新 UI。当数据发生变化时,UI 将自动更新。 LiveData 的优点包括: * 它提供了一种简单的方法来处理数据更新,使得组件之间的通信更加清晰和一致。 * 它允许你将数据传递给任何需要它的组件,而无需手动复制或传递数据。 * 它支持多线程操作,可以在异步操作中安全地更新 LiveData 对象。 需要注意的是,LiveData 是用于在后台线程中维护数据的,并且不能被外部观察者修改。因此,如果你需要修改数据并通知外部观察者,你可能需要使用其他方法,如使用 ViewModel 或其他观察者模式实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值