LiveData 是一种具有生命周期感知能力的、可观察数据的、持有类。本文将从以下几个方面来介绍 LiveData:
- LiveData 是什么?为什么要使用 LiveData?
- 分析 LiveData 的组成及实现原理
- 介绍 LiveData 中的转换方法
- 总结使用 LiveData 的步骤
1. LiveData 是什么?为什么要使用 LiveData?
LiveData 可以简单的理解为具有感知生命周期能力的容器。生命周期感知能力的实现,得益于 Android Framework 使用了一下 Lifecycles 中的类。关于 Lifecycle 的详细介绍,可以查看此前的一篇文章 Android 架构组件之 Lifecycle
这里我们简单的总结一下,Lifecycle 组件包括三个组成部分:
- Lifecycle:Lifecycle是一个定义 Android 生命周期及状态的类。
- LifecycleOwner 接口,用于连接有生命周期的对象,例如 AppCompatActivity 和 Fragment。
- LifecycleObserver,用于观察 LifecycleOwner 的接口。
而我们要介绍的 LiveData,是一个 LifecycleObserver,它可以直接感知 Activity 或 Fragment 的生命周期。基于此,使得 LiveData 较其它的容器类,有一下两个优势:
- 如果Activity 不在屏幕上,LiveData 不会触发没有必要的界面刷新;
- 如果 Activity 已经被销毁,LiveData 将自动清空与 Observer 的连接。这样屏幕外或者已经销毁 Activity 或 Fragment就不会被意外地调用。
1.1 一个示例,看使用 LiveData 的优势
下面来看一个示例。假设有一个 UI 界面和 LiveData 对象,这个对象保存了你想要在屏幕上显示的数据,界面可以声明“观察”LiveData 对象,也就是说,界面希望在 LiveData 有更新时收到通知,随后界面会使用新数据重新进行绘制。简而言之,LiveData 可以使屏幕上显示的内容与数据随时保持同步。 看示例代码:
LiveData 对象通常保存在 ViewModel 类中,关于 ViewModel 的介绍,可以查看Android 架构组件之 ViewModel
class LiveDataTimerViewModel : ViewModel() {
private val mElapsedTime = MutableLiveData<Long>()
private var mInitialTime: Long = 0
private var timer: Timer? = null
init {
mInitialTime = SystemClock.elapsedRealtime()
timer = Timer()
// Update the elapsed time every second.
timer?.scheduleAtFixedRate(object : TimerTask() {
override fun run() {
val newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000
// setValue() cannot be called from a background thread so post to main thread.
mElapsedTime.postValue(newValue)
}
}, ONE_SECOND, ONE_SECOND)
}
fun getElapsedTime(): LiveData<Long> = mElapsedTime
在 ViewModel 中,声明一个 MutableLiveData 类型的成员变量,用来保存屏幕上要显示的数据,通过 Timer 类,每秒钟更新一次 LiveData 中的数据。
接下来,在 Activity 的 onCreate()方法中,我们可以从 ViewModel 中获取 LiveData,并在 LiveData 上调用 observe()方法,方法中的第一个参数为 Context,在这里为当下的 Activity,第二个参数是“Observer 观察者”,属于方法回调,回调之后界面会被更新,如果需要更新 LiveData,可以调用 setValue()方法或 postValue()方法。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.chrono_activity_3)
mLiveDataTimerViewModel = ViewModelProvider(this).get(LiveDataTimerViewModel::class.java)
subscribe()
}
private fun subscribe() {
val elapsedTimeObserver = Observer<Long> {
val newText = resources.getString(
R.string.seconds, it
)
timer_textview.text = newText
Log.d("ChronoActivity3", "Updating timer");
}
mLiveDataTimerViewModel?.getElapsedTime()?.observe(this, elapsedTimeObserver)
}
完整的示例代码,在文末给出 GitHub 地址。
setValue()方法或 postValue()方法,两者的区别在于,setValue 只可以在主线程上运行,postValue 只可以在后台线程上运行,调用 setValue 和 postValue 时,LiveData 将通知 Observer,并更新界面。
以上是LiveData的基本介绍以及使用它的优势,除此之外。Room 数据框架可以很友好地支持 LiveData,Room 返回的 LiveData 对象,可以在数据库数据变化时自动收到通知,还可以在后台线程中加载数据,这样我们可以在数据库更新的时候,轻松地更新界面。关于 Room 组件的介绍,可以查看我们的系列文章。
2. 分析 LiveData 的组成及实现原理
2.1 通过类图分析 LiveData 的组成
我们用最新的 lifecycle-livedata:2.2.0-alpha02 版本来分析其原理,先看一张类图。
-
在 LiveData 中定义了三个内部类 ObserverWrapper、LifecycleBoundObserver 和 AlwaysActiveObserver。
-
ObserverWrapper 是 Observer 的一个包装类,在该类中会记录组件是否处在激活状态的信息,shouldBeActive() 方法是一个抽象方法,需要子类去实现,activeStateChanged() 方法会根据组件的状态,回调不同的方法,后面会详细介绍。
-
LifecycleBoundObserver 和 AlwaysActiveObserver 是 ObserverWrapper的子类,LiveData 对组件的注册监听,都是通过这两个类完成的,区别是当使用 observe()方法注册时使用的是 LifecycleBoundObserver,使用observeForever()方法注册时使用的是 AlwaysActiveObserver。LifecycleBoundObserver 还实现了 LifecycleEventObserver 接口,这样LifecycleBoundObserver 就可以通过 onStateChanged()回调方法来监测组件生命周期的变化了。
-
LiveData 是一个抽象类,它有两个子类 MutableLiveData 和 MediatorLiveData,在 LiveData 中有个两个用来完成对组件注册监听的方法 observe() 和 observeForever();有注册监听的方法,就有取消注册监听的方法 removeObserver() 和 removeObservers();因为 LiveData 是一个容器,所以提供了两个设置 Value 的方法 postValue() 和 setValue();onActive() 和 onInactive()是两个回调方法。
-
LiveData 中的两个回调方法 onActive()和 onInactive(),当LiveData 监听的处于激活状态的组件中 observer的个数不为 0 时会调用回调函数onActive(),当 LiveData监听的组件没有处于激活状态,或者组件中 observer 的个数为 0 时调用onInactive()回调函数。
2.2 分析 LiveData 是怎样感知组件生命周期的
在上面的类图中,我们了解了跟 LiveData 相关的类,下面我们来看一下 LiveData 是怎样感知组件生命周期的,当数据更新时又是怎样通知组件的。
先来看 LiveData 是怎样感知组件生命周期的,上面提到了在 LiveData 中有两个用来完成对组件注册监听的方法 observe() 和 observeForever(),先来看 observe()方法:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
observe 的接收一个 LifecycleOwner对象,组件 Activity 和 Fragment 都是实现了LifecycleOwner接口,所以这里的owner可以理解为组件对象。在方法内部创建了LifecycleBoundObserver 对象 wrapper,通过
owner.getLifecycle().addObserver(wrapper)
完成了对组件生命周期的监听,当组件生命周期发生变化时都会回调 wrapper 对象中的onStateChanged()方法,再来看一下 LifecycleBoundObserver 的实现:
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
可以看到在 LifecycleBoundObserver 中有一个 shouldBeActive()方法,这个方法会在组件状态是STARTED 和 RESUMED 时返回 true。
在 onStateChanged() 方法中会调用到 activeStateChanged()方法,该方法接收一个 boolean 型参数,当参数值为 true 时会调用dispatchingValue()方法,这个方法最终会调用 Observer 的onChanged()回调方法。这也就解释了使用 observe()方法对组件进行监听,只有组件在激活状态STARTED、RESUMED时(对应生命周期 onStart、onResume、onPause)时,才会收到数据更新的通知。当组件在DESTROYED状态时,在onStateChanged()方法内部会调用removeObserver(),因此我们在使用 observe()方法时,可以不考虑 observer 的销毁。
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
我们前面提到使用 observeForever 注册时需要手动取消,来看看是为什么。
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true);
}
observeForever() 方法不再接收 LifecycleOwner 对象,在AlwaysActiveObserver 类中的 shouldBeActive()方法的返回值一直是 true,所以使用该方法进行注册监听时,只要数据有变化,不论组件处在什么状态,都会收到通知,这也是为什么要在退出组件时,需要手动调用 removeXXX()方法将注册的 observer 移除。
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
2.3 数据更新时 LiveData 又是怎样通知组件的
了解了 LiveData 是怎样感知组件生命周期的,下面再来看一下当数据更新时又是怎样通知组件的。LiveData 提供了两个方法来更新数据,setValue() 方法和 postValue() 方法。区别是 setValue() 方法要在主线程中调用,而 postValue() 方法既可在主线程也可在子线程中调用。先来看一下 setValue() 方法。
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
setValue() 方法中会调用 dispatchingValue() 方法,这个方法最终会调用observe() 或者 observeForever() 传入的Observer对象的 onChanged()回调方法。这就解释了当数据更新时是怎样发送通知的。再来看一下 postValue()方法:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
可以看到 postValue() 方法通过 ArchTaskExecutor 实现了在主线程中执行mPostValueRunnable 对象中的内容,而在mPostValueRunnable中最终会调用setValue() 方法来实现改变 LiveData 存储的数据。
至此,我们知道了 LiveData 是怎样感知组件生命周期的,以及当数据更新时又是怎样通知组件的。
3. LiveData 中的转换方法
LiveData 还提供各种转换方法,包括: map() 、switchMap() 和 MediatorLiveData。
3.1 map
map()方法,可转换 LiveData 的输出,map 可以将 LiveData A 的方法传递到 LiveData B。
比如,上面的示例中将 LiveData 中的 Long 类型,通过 map 方法转换为 String 类型。
var userNameLiveData = Transformations.map(mElapsedTime, { time ->
"$time 秒"
}
3.2 switchMap
switchMap() 更改被 LvieData 观察的对象,switchMap与 map 很相似,区别在于,switchMap 给出的是 LiveData,而 Map 给出的是具体值。
举个例子,假设在 Room 数据库中存有很多用户,我们有一个查找数据库用户的方法,我们可以通过 switchMap 将用户 ID与 LiveData 相连,当界面需要查找的 ID 发生变化时,会重新调用查找用户的方法,调用之后生成 LiveData。现在将引用到新找到的用户的 LiveData,这样不管需要查找多少个用户的 LiveData,界面都只需要观察生成的 LiveData 一次。这就是 switchMap 的强大之处。
3.3 MediatorLiveData
MediatorLiveData:用于自定义数据转换,它可以添加或者移除原来的 LiveData 对象,然后多个原 LiveData 对象可以进行组合,作为单一 LiveData 向下传递。
4. 总结使用 LiveData 的步骤
我们先来总结一下使用 LiveData 的步骤:
-
创建 LiveData 实例以存储某种类型的数据,通常在 ViewModel 类中完成。
-
创建可定义 onChanged() 方法的 Observer 对象,该方法在 LiveData 对象存储的数据更改时会被调用。通常情况下,在界面(如 Activity 或 Fragment)中创建 Observer 对象。
-
使用 observe() 方法将 Observer 对象附加到 LiveData 对象。这样会使 Observer 对象订阅 LiveData 对象,以使其收到有关更改的通知。通常情况下,您可以在界面(如 Activity 或 Fragment)中附加 Observer 对象。
LiveData使用起来非常简单,它可以被观察,可以感知生命周期,在很多情况下都能够被灵活运用。
更多内容,可以订阅 我的博客