本文翻译自【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)。 在上一篇文章中,做者提到了 ViewModel
和 LiveData
,其中 LiveData 是用来从 ViewModel 层向 View 层传递数据。但当时并无完整地介绍 LiveData 的做用,如今咱们来详细看下 LiveData 的定义和使用。 那么,LiveData 有什么特别的地方呢?android
官方 定义是:git
LiveData 是一个可被观察的数据持有类。与普通的被观察者(如 RxJava 中的 Observable)不一样的是,LiveData 是生命周期感知的,也就是说,它能感知其它应用组件(Activity,Fragment,Service)的生命周期。这种感知能力能够确保只有处于 active 状态的组件才能收到 LiveData 的更新。详情可查看 Lifecycle。github
这就是官方对 LiveData 的定义。 为了简单起见,咱们先来看一些以前的开发习惯,以便更好地理解。数据库
当 Android 刚诞生的时候,大多数开发者写的代码都放在一个 Activity 中。 bash
然而,把全部逻辑都放在一个 Activity 类中并不理想。由于 Activity 很难进行单元测试。 鉴于此,业界出现了MVC、MVP、MVVM 等开发架构,经过 Controller、Presenter、ViewModel 等分层抽离 Activity 中的代码。 网络
这种架构能把逻辑从 View 层分离出来。然而,它的问题是 Controller、Presenter、ViewModel 等不能感知 Activity 的生命周期,Activity 的生命周期必须通知这些组件。 为了统一解决方案,Google 开始重视这个问题,因而 Architecture Components 诞生了。 其中 ViewModel 组件有一个特殊能力,咱们不须要手动通知它们,就能够感知 Activity 的生命周期。这是系统内部帮咱们作的事情。 session
除了 ViewModel 外,用于从 ViewModel 层暴露到 View 层的数据,也有生命周期感知的能力,这就是为何叫作 LiveData
的缘由。做为一个被观察者,它能够感知观察它的 Activity 的生命周期。架构
为了更好地理解,下图将 LiveData 做为数据中心: app
从上图能够看到,LiveData 的数据来源通常是 ViewModel,或者其它用来更新 LiveData 的组件。一旦数据更新后,LiveData 就会通知它的全部观察者,例如 Activity、Fragment、Service 等组件。可是,与其余相似 RxJava 的方法不一样的是,LiveData 并非盲目的通知全部观察者,而是首先检查它们的实时状态。LiveData 只会通知处于 Actie 的观察者,若是一个观察者处于 Paused 或 Destroyed 状态,它将不会受到通知。 这样的好处是,咱们不须要在 onPause
或 onDestroy
方法中解除对 LiveData 的订阅/观察。此外,一旦观察者从新恢复 Resumed 状态,它将会从新收到 LiveData 的最新数据。ide
LiveData
是一个抽象类,咱们不能直接使用。幸运的是,Google 提供了一些其简单实现,让咱们来使用。
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 pausing 或 terminating 时解除订阅。 即便咱们没有解除订阅,也不会有什么问题。看下面的例子,当 Fragment 销毁时,LiveData 不会由于产生一个新数据(1428)通知给 inactive 的 Fragment 而崩溃(Crash)。
同时,也能够看到当 Fragment 从新 active 时,将会收到最新的 LiveData 数据:1428。
咱们通常定义一个 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
。接下来看一看这是个什么东西。
从 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 就能够同时接收到 LiveDataA
和 LiveDataB
的数据变化,以下图所示:
有一点须要注意的是:当 Fragment 不 再处于 active
状态时,若是 LiveDataA
和 LiveDataB
的数据都发生了变化,那么当 Fragment 从新恢复 active 状态时,MediatorLiveData
将获取最后添加的 LiveData 的数据发送给 Fragment,这里即 LiveDataB
。
从上图能够看到,当 Fragment 恢复活动状态时,它就会收到 LiveDataB
的最新数据,不管 LiveDataB
变化的比 LiveDataA
变化的早或晚。从上面代码能够看到,这是由于 LiveDataB
是最后被添加到 MediatorLiveData
中的。
上面的示例中展现了咱们能够同时监听两个 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,您能够经过如下平台找到我: