LiveData是一个用于构建数据流的响应式框架,简单来讲的话,它就相当于一个砍掉了所有操作符的RxJava。
LiveData框架中的数据源为LiveData
类,这是一个泛型类,T
是它持有的数据的类型。它的API如下
public abstract class LiveData<T> {
public T getValue()
public boolean hasActiveObservers()
public boolean hasObservers()
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)
@MainThread
public void observeForever(@NonNull Observer<? super T> observer)
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer)
@MainThread
public void removeObservers(@NonNull final LifecycleOwner owner)
}
LiveData
主要声明了注册和反注册的接口,注册有两种方式,一是使用observe
方法,这个方法需要LifecycleOwner
参数,这个方法注册的订阅者,只有在owner
的生命状态处于STARTED
或者RESUMED
状态的时候才会收到数据改变的回调;还有一种注册方式是observeForever
方法,这种方式注册的订阅者就是一直生效的,所以可能会造成内存泄漏,需要及时调用removeObserver
方法反注册,相对应的,还有removeObservers
方法反注册这个LifecycleOwner
的所有订阅者。
LiveData
的订阅者Observer
的接口如下:
public interface Observer<T> {
void onChanged(T t);
}
当LiveData
持有的数据改变时,它的所有(活跃的)订阅者的onChanged
的方法会被调用,最新的值作为参数传给订阅者。
细心的读者会发现LiveData
类提供了读取值的接口,却没有提供写入值的接口,这是因为LiveData
将它设置值的方法的访问级别设为了protected
:
@MainThread
protected void setValue(T value)
protected void postValue(T value)
LiveData
有两个方法可以写入值,其中setValue
只能在主线程调用,而postValue
则可以在子线程中调用。
写入的两个方法的访问级别在LiveData
的子类MutableLiveData
开放:
public class MutableLiveData<T> extends LiveData<T> {
...
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
MutableLiveData
唯一的改变就是将两个写入方法公开了。
MutableLiveData
已经具有了读取、写入值,注册、反注册订阅者的完整功能。而它的子类MediatorLiveData
还提供了LiveData
之间的订阅功能:
public class MediatorLiveData<T> extends MutableLiveData<T> {
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged)
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote)
}
MediatorLiveData
声明了addSource
接口,当source
的值改变时,会调用onChanged
回调,MediatorLiveData
可以在onChanged
中根据source
的值更新自己的值,这样就构成了LiveData
的连续响应。
LiveData
同样是基于订阅者模式实现的,而它和Lifecycles的区别是,LiveData框架中没有一个独立管理类,管理的功能也由被订阅者LiveData
内部实现了,所以接下来,我将基于LiveData
和它的子类来分析LiveData框架的实现原理。
LiveData
虽然是个抽象类,但它实际上已经将基本的功能和框架都实现了。它使用int
属性mVersion
来记录当前数据的版本号,使用SafeIterableMap
类型的属性mObservers
管理所有订阅者(这个类在上一节有过介绍,这里就不赘述了),其中ObserverWrapper
是Observer
的封装类,它增加了一个mLastVersion
属性和shouldBeActive
抽象方法,只有在mLastVersion
小于mVersion
且shouldBeActive
返回true时,LiveData
才会在更新数据时回调这个Observer
。
ObserverWrapper
有两个子类:LifecycleBoundObserver
和AlwaysActiveObserver
,分别对应了observe(LifecycleOwner owner, Observer super T> observer)
和observeForever(Observer super T> observer)
两个接口。
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
final LifecycleOwner mOwner;
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
...
}
private class AlwaysActiveObserver extends ObserverWrapper {
@Override
boolean shouldBeActive() {
return true;
}
}
LifecycleBoundObserver
会根据传入的LifecycleOwner
的生命状态来判断是否Observer
是否应该受到回调,并且当它的生命周期到了DESTROYED
的时候,还会自动调用反注册方法取消注册。
接下来看LiveData
更新数据的实现,先说postValue
的实现,它是使用Handler在主线程执行mPostValueRunnable
来更新值的,最终调用的还是setValue
方法。
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() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
这里还有一个小细节,因为postValue
可能存在多个线程同时调用的情况,所以为了少执行mPostValueRunnable
,Google将Handler的调用移到了同步锁之外,通过mPendingData
是否等于 NOT_SET
来判断是否有值已经赋给了mPendingData
,但还没有同步到newValue
(即有一个mPostValueRunnable
在队列中还未执行),进而决定是不是还需要调用mPostValueRunnable
。这种做法造成的后果是当postValue
同时调用多次时,mPostValueRunnable
只调用一次,mPendingData
的值会等于最后一个获取到锁的线程设置的值,即其他线程设置的值都会丢失。这个是使用时要注意的。
而在setValue
中,在mVersion
+1之后,LiveData
紧接着就是调用dispatchingValue
方法来通知所有订阅者:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
dispatchingValue
方法有一个参数,当initiator
为null时,表示是LiveData
更新了值在通知所有订阅者,不为null时,表示是有新订阅者注册,需要将当前值发送给新的订阅者。除此之外,mDispatchInvalidated
和mDispatchingValue
同样是处理嵌套请求的情况,这里也不赘述了。
因为MutableLiveData
的实现只是开放了setValue
和postValue
接口,就不多说了,直接看它的子类MediatorLiveData
的实现,前面已经提到,MediatorLiveData
增加了addSource
接口,来实现LiveData
之间的订阅:
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
...
if (hasActiveObservers())
e.plug();
}
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.observeForever(this); }
void unplug() { mLiveData.removeObserver(this); }
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
在addSource
方法中,MediatorLiveData
会将数据源source
封装成实现了Observer
接口的Source
类对象,调用plug
方法订阅source
,然后在Source
的onChanged
方法中调用addSource
方法中传入的onChanged
回调。接下来source
的值的变化就都会通知到onChanged
,直到调用removeSource
方法,调用unplug
方法解除和source
的绑定为止。
除了上述绑定和解除绑定的过程,MediatorLiveData
中还有两个方法会影响到它和数据源的订阅关系:
@Override
protected void onActive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().plug();
}
}
@Override
protected void onInactive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().unplug();
}
}
onActive
和onInactive
在LiveData
中声明,其中onActive
会在LiveData
获得第一个订阅者(包括之前有订阅者但是又已经全部反注册的情况)之后调用,onInactive
会在LiveData
失去了所有订阅者的时候调用。所以当MediatorLiveData
失去了所有订阅者之后,它会解除和所有数据源的订阅关系,直到又有新的订阅者注册再重新注册所有数据源。
我在最开头的时候说过,LiveData框架相当于一个砍掉了所有操作符的RxJava,这是LiveData的设计思想决定的,虽然这种“小而美”的思路很好,但是有时候我们在做LiveData数据流处理的时候,还是会需要那些操作符,因此Google提供了Transformations工具类来提供一些操作符:
public class Transformations {
@MainThread
public static <X> LiveData<X> distinctUntilChanged(LiveData<X> source)
@MainThread
public static <X, Y> LiveData<Y> map(LiveData<X> source,final Function<X, Y> mapFunction)
@MainThread
public static <X, Y> LiveData<Y> switchMap( LiveData<X> source,final Function<X, LiveData<Y>> switchMapFunction)
}
它们的实现我就不多说了,简单介绍一下它们的功能,相信熟悉RxJava的朋友不难看出它们的原型:
source
,只有当前值跟之前的值不一样的时候才会触发回调。上述类已经构建了一个完整的响应式框架,但是我在看源码的时候,发现居然还有一个ComputableLiveData类!这个类虽然名字里面带了“LiveData”,但它并没有继承LiveData
类,而是有一个提供LiveData
的接口:
public abstract class ComputableLiveData<T> {
public ComputableLiveData(@NonNull Executor executor)
public LiveData<T> getLiveData()
public void invalidate()
protected abstract T compute();
}
从实现上来说,ComputableLiveData
可以被视作一个FutureTask
,区别在于,它没有get
接口,取而代之的是getLiveData
接口,它会执行抽象方法compute
,然后通过LiveData
返回结果。ComputableLiveData
还提供了invalidate
方法来使原来的值无效化并重新调用compute
计算值。
ComputableLiveData
类看起来并不是LiveData框架的组件,更像是Transformations类这样的工具类。
LiveData可以说是学习成本最低的响应式框架了,虽然这么说,但它还是我这系列文章里面实现最复杂的。在学习它的源码的时候,给我感触最深的是它最难的不是代码实现,而是理解它的设计思路,这已经是在去掉了操作符的情况下,由此可见RxJava是多么的复杂!但是剥去层层的外衣可以看到,LiveData最终还是基于简单的订阅者模式。抓住订阅、发布两条线来走流程,并且注意看它是怎么解决这类框架都会遇到的问题,理解起来才更轻松,这终究还是一个经验积累的过程。