LiveData
1,LiveData简介
LiveData是Jetpack库集合中的一员,从名字看来,它是一种数据。更准确的说,它是一种数据容器。它提供了一些功能,使得这种数据不仅仅是简单的数据,而是一种可观测的、可感知组件生命周期的数据。
public abstract class LiveData<T> {...}
LiveData
是一种可观测的数据,我们可以向LiveData中注册观察者Observer
,那么当LiveData的数据发生改变的时候,就会主动通知观察者(调用Observer的onChanged方法)。并且,这种通知是可以遵循组件生命周期的,当生命周期状态至少STARTED
的时候LiveData才会去通知它,否则的话不去发送。但是,当组件重新恢复活跃状态的时候,LiveData就会去通知它,然后就能够拿到最新的数据。
这里的STARTED的指的是Lifecycle的states,一共有5种状态,分别是:INITIALIZED,DESTROYED,CREATED,STARTED,RESUMED,不了解的可以先去看一下Jetpack的另一个组件Lifecycle
活跃状态指的是Lifecycle的状态至少处于STARTED,也就是STARTED和RESUMED,只有这两种状态才会被LiveData认为是活跃状态,才能够接收到LiveData的最新消息。
LiveData会根据与观察者绑定的组件的生命周期状态去选择是否通知观察者数据发生了更新,因此LiveData要求在注册观察的时候,需要传递一个LifecycleOwner
参数。LiveData通过这个参数去获取它的生命周期状态,然后决定是否要将事件发送给对应的观察者。而注册时的另一个参数是Observer,它是一个单方法接口,LiveData通过它的onChanged
方法去通知最新的数据。
public interface LifecycleOwner {
// LiveData根据这个方法去拿到Lifecycle从而获取生命周期状态
@NonNull
Lifecycle getLifecycle();
}
public interface Observer<T> {
// 当LiveData的值修改后将会调用这个方法,参数t就是LiveData的值
void onChanged(T t);
}
2,使用LiveData
只需要在build.gradle中声明以下即可引入livedata的2.2.0版本,后面的源码都是基于此版本。
// Kotlin项目
implementation ‘androidx.lifecycle:lifecycle-livedata-ktx:2.2.0’
// Java项目
implementation ‘androidx.lifecycle:lifecycle-livedata:2.2.0’
LiveData是一个抽象类,并且它对外修改数据的两个方法setValue/postValue
都被标记为protected,因此它是不可修改的,实际中常使用它的子类MutableLiveData
。MutableLiveData相对于LiveData没有其他大的改变,只是将setValue和postValue标记为了public
而已。这两个方法中setValue是用作主线程修改数据的,postValue可以在子线程中修改LiveData的值。
class MainActivity : AppCompatActivity() {
private val stringLiveData = MutableLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
stringLiveData.observe(this, Observer {
textView.text = it
})
btnChange.setOnClickListener {
stringLiveData.value = "value changed"
}
}
}
上面是使用LiveData的一个最简单的案例,在MainActivity中声明了一个stringLiveData,并且在onCreate中注册观察者,观察事件就是当LiveData的值发生改变的时候,将textView设置为这个值。当按钮btnChange点击的时候,会将LiveData的值修改为“value changed”,这时LiveData就会通知观察者,因此就会将textView设置为value changed。
注意,LiveData是可以感知组件的生命周期的,这就意味着它应该比组件存在的时间更长一点,那么我们在Activity中去定义LiveData就是一个不恰当的做法,实际中应该在ViewModel
中或者在单例中去定义LiveData。
在上例中就使用MainActivity
与观察者一起注册在LiveData
中,因此这个观察者Observer
能否接收到消息就取决于MainActivity的生命周期了。Fragment中也可以传入this,但是Fragment中还有一个参数viewLifecycleOwner
,它也是LifecycleOwner
。
viewLifecycleOwner
与Fragment
本身生命周期有一点不同,ViewLifecycleOwner是专注于View的生命周期,它在onCreateView之后才会被创建,此时处于INITIALIZED状态。在onActivityCreated后为CREATED状态,在onDestroyView之后处于DESTROYED状态。也就是说**,viewLifecycleOwner的生命周期状态是在onCreateView到onDestroyView之间**。
而Fragment本身更专注Fragment的生命周期,它在构造方法中初始化处于INITIALIZED,然后在onCreate后处于CREATED,在onDestroy后处于DESTROYED状态。也就是说使用Fragment本身的生命周期状态是在构造方法到onDestroy之间。
那么我们在Fragment注册观察者的时候就要注意选择viewLifecycleOwner
还是this
。我们知道在使用ViewPager
和FragmentPagerAdapter
的时候,默认缓存左右各一个Fragment,当切换的时候,会触发Fragment的onPause、onStop、onDestroyView
。若是我们此时在Fragment观察LiveData的时候使用的是this,那么在ViewPager切换回来的时候在onCreateView
中又会去注册一次(前面说过在onCreateView之后才能使用viewLifecycleOwner),这样重复注册就会在成当LiveData修改后收到多次事件的情景。
总结:Fragment中关乎view的使用viewLifecycleOwner(大部分情况我们都是使用viewLifecycleOwner),不在乎View而专注Fragment的可以使用this(例如我们添加一个不带界面的Fragment,这种情况也不少,比如Glide就是通过创建一个无界面的Fragment来管理什么时候去加载图片什么时候停止加载,比如还可以通过空的Fragment去申请权限等不需要界面的操作,这时候就应该用this而不是viewLifecycleOwner)。
3,LiveData源码分析
3.1 ,注册成为LiveData的一名观察者
从前面那个简单的例子可以看出,要观察到LiveData的数据变更必须要先注册成为LiveData的一名观察者,这样才能接收到数据的变化。
// MainActivity
stringLiveData.observe(this, Observer {
textView.text = it
})
//LiveData
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
// 首先拿到生命周期状态,处于DESTROYED时不去绑定,该状态是onDestroy后的状态
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 将LifecycleOwner与Observer绑定在一起
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 从已经注册集合中查找是否已经注册过,未注册的会添加到mObservers中并且返回null
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 若是已经注册过,并且注册的owner与当前不一致。这种情况是这样:定义一个Observer事件,然后在Activity中注册,
// 然后又使用这个Observer在另一个Activity中注册在同一个LiveData上。这里的Activity指实现LifecycleOwner的观察者。
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);
}
继续看前面的例子,首先在Activity中注册通过LiveData的observe
方法进行注册,参数传递了两个,一个是this
本身,一个是Observer
接口对象。在observe方法中,首先会拿到LifecycleOwner的生命周期状态,并且忽略处于DESTROYED
状态的观察者的注册信息,而且一个观察者还只能与一个LifecycleOwner进行绑定注册。
然后,在实际注册中,是将LifecycleOwner和Observer绑定在一起成为一个wrapper
,这时候,wraper就可以看作是一个拥有声明周期的观察者。接下来先注册到LiveData中,再注册到Lifecycle
中去感知生命周期的变化。
这个wrapper是LifecycleBoundObserver
类型,它最终实现了LifecycleObserver接口,因此它可以被添加在Lifecycle中,这样当观察者的生命周期状态发生变化的时候,就会通知到wrapper的onStateChanged
方法,从而去执行一系列的操作。至于为什么会调用到这个方法,就要自己去看一下Lifecycle的实现了。
// LiveEventObserver
public interface LifecycleEventObserver extends LifecycleObserver {
void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event);
}
// LiveData.LifeCycleObserver
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
...
}
上面介绍的就是LiveData的注册流程,简单总结一下就是将观察者与LifecycleOwner包装成一个LifecycleBoundObserver
,然后同时注册在LiveData中和Lifecycle中。注册在LiveData是为了去通知观察者,注册在Lifecycle中是为了感知生命周期的变化。
3.2,修改LiveData的值,获取事件通知
对LiveData的使用从前面也就看出来了,主要就是注册成为观察者,然后修改LiveData的值使得观察者获得更新数据的通知。LiveData提供了两个方法去修改数据,setValue/postValue
,前者只可以在主线程中修改,后者可以在子线程中修改。实际上postValue也是通过handler发送一个Runnable
,这个runnable中最终又调用了setValue去修改数据。因此下面直接分析setValue方法:
// LiveData
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
// 版本加1,这个版本是LiveData的修改版本,每次修改后版本+1,初始值在构造方法中设置,默认为-1
// 带值的构造方法默认为已经修改了一次
mVersion++;
mData = value;
dispatchingValue(null);
}
static final int START_VERSION = -1;
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
setValue比较简单,它首先记录了新值并增加了版本号,然后调用了dispatchingValue
方法去分发新value,通知观察者value被修改了。而版本号则是用来控制数据更新的,它每次更新数据的时候,这个版本号就会+1
,我们可以通过这个版本号获取LiveData经历了几次数据的修改。另外,包装类的观察者也是有一个版本号字段的,它是用来判断当前数据是否是最新的,因为每次LiveData向观察发送新数据事件的时候都会同步一下这个版本号,因此可以通过这个字段来判断观察者有没有拿到最新的数据。
//LiveData
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// 若是已经在分发事件了,则直接返回,防止多次分发事件,如连续两次setValue,则每个观察者只能触发一次事件,
// 第一次修改的值不会被分发给观察者
if (mDispatchingValue) {
// 这个值是用来控制分发的,若是第一次setValue的时候,已经有一部分观察者分发过事件了,则第二次setValue的时候,
//会重新重头开始发送事件。也就是说,连续两次的setValue有可能部分观察者能收到两次事件,而部分观察者只能收到一次。
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// 若是参数非空,则只分发给参数(参数是一个观察者)本身,否则去分发给所有的观察者
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// 遍历存储观察者的map集合,然后通知事件
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
方法进行了一系列的操作用来尽可能得保证事件不被分发多次。因为修改value的方法只能从主线程访问,所以为了性能这里没有用其他复杂的操作去进行同步,而是简单地使用了两个参数进行控制。
从这个方法可以看出,dispatchingValue
只是通知LiveData去分发事件,它循环遍历每一个观察者,然后通过considerNotify
去实际通知具体的观察者。
// LiveData
private void considerNotify(ObserverWrapper observer) {
// 观察者处于非active状态时不发送事件
if (!observer.mActive) {
return;
}
// shouldBeActive前面有说过,它是获取观察者的生命周期状态,并处于STARTED和RESUMED的时候才会返回true
// 这里又进行了一次判断,是为了避免出现这种情况:生命周期已经变为非活跃状态了,但是还没收到状态变化的事件,
// 因此直接手动去将观察者的状态标记为非活跃状态,并且不发送消息
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
// 同步版本号
observer.mLastVersion = mVersion;
// 通知观察事件
observer.mObserver.onChanged((T) mData);s
}
简单总结一下:当LiveData的值修改后,会通过dispatchingValue
方法去决定是通知某个观察者还是所有的观察者(根据参数是否为null来决定),然后通过considerNotify去具体通知某个观察者。而considerNotify
中就会根据观察者的生命周期状态来决定是否发送消息(调用他的onChanged
方法)。
3.3,生命周期变化导致的事件分发
前面提到,当value修改后通知观察者的时候,LiveData会判断观察者是否处于活跃状态,通过mActive
参数来判断。但是,我们已经看了LiveData从注册到修改的整个流程,并没有发现mActive的修改部分,也就是说,mActive是自动修改的。而mActive是用来判断观察者活跃状态的,那肯定与观察者的生命周期相关,并且我们注册的时候还将包装类wrapper注册在了Lifecycle
中,因此,mActive肯定与Lifecycle相关。
由于这个wrapper实现了LifecycleEventObserver
接口,那么我们知道,当Lifecycle的生命周期状态发生变化的时候,肯定会调用onStateChanged
方法,这里涉及到Lifecycle的知识,不知道的话可以先看一下Lifecycle的实现。
// LifecycleBoundObserver
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
// DESTROYED状态下移除注册
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// 触发观察者状态变化
activeStateChanged(shouldBeActive());
}
//LiveData
void activeStateChanged(boolean newActive) {
// 状态一致则不修改
if (newActive == mActive) {
return;
}
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
// 若是active则+1,否则-1,这个值用来统计LiveData中注册的观察者的活跃的个数
LiveData.this.mActiveCount += mActive ? 1 : -1;
// 当前0个活跃观察者,分发了一个活跃者。也就是说,LiveData的观察者的活跃个数从0到1 的时候会触发,默认空实现
if (wasInactive && mActive) {
onActive();
}
// 这种情况是活跃个数从1到0,也就是说当LiveData没有活跃观察者时调用,默认空实现
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
// 对于恢复活跃的观察者,通知分发事件。这也就是为什么当观察者每次回复活跃状态的时候都能接收到最新数据
// 这里的参数是观察者本身,因此在发送消息的时候,只会给这个观察者发送消息
if (mActive) {
dispatchingValue(this);
}
}
在onStateChanged
中,每次收到生命周期事件的时候都会去判断观察者是否处于了DESTROYED
状态,若是的话,则会将它从Lifecycle中移除(因此我们只需要向LiveData注册观察者就行,移除的事交给它自己去管理),否则就通过activeStateChanged
通知状态的改变。
在activeStateChanged
中,会记录观察者的活跃状态和当前活跃的观察者的数量,并且在活跃的状态下,去通知LiveData分发给观察者最新的数据。这样,每次观察者的状态转化成active
的时候就能接收到最新的数据,而非active
的时候则不会收到数据的通知。
// LiveData
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();
removed.activeStateChanged(false);
}
// LifecycleBoundObserver
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
在removeObserver
中,会将观察者从LiveData中移除,然后调用detachObserver
方法将观察者从Lifecycle中移除。这时候就已经完全从移除了观察者,接下来又调用一次activeStateChanged(false)
将它本身标记为非活跃状态从而去同步LiveData中对观察者的活跃数量的变化。并且由于DESTROYED
的时候会自动去移除观察者,我们就不用手动去移除观察者,这极大的方便了开发者的编码并且还不用担心内存泄露问题。
3.4,observeForever忽略观察者的生命周期
前面讲了LiveData的使用,以及从源码分析它是如何实现的。但我们注册成为LiveData的时候使用的是LiveData.observe(LifecycleOwner,Observer)
,这个方法要求在传入观察事件的时候同时传入一个LifecycleOwner
参数,LiveData就是根据这个参数去获取它的生命周期,并根据生命周期去选择发送事件或者移除观察者。
除了这种注册方式,LiveData还提供了一种不关注生命周期的注册方式,即通过LiveData.observeForever(Observer)
去注册观察者。这个方法跟前面讲的observe方法相比,少了一个LifecycleOwner参数。因此对于这种方式注册的观察者,LiveData并不知道它对应的生命周期状态,所以当LiveData的value发生改变的话,就会一直认为它是处于活跃状态的,也就会一直收到消息,直到我们手动将它移除。
// LiveData
@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);
}
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);
}
比较两种注册方法,发现在observer中,包装对象类型是LifecycleBoundObserver
,而observeForeve中,包装对象类型却是AlwaysActiveObserver
。并且最后并没有将包装类型注册到Lifecycle中,而是通过activeStateChanged
将自己的状态标记为active状态。由于AlwaysActiveObserver并没有被注册到Lifecycle中,因此它的活跃状态不会发生改变,一直保持为注册时修改的活跃状态。
// AlwaysActiveObserver
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
这个包装对象太简陋了,仅仅是将shouldBeActive直接返回true
,而在LifecycleBoundObserver中这个方法是根据生命周期状态来决定返回true/false
的。我们知道当LiveData的value修改的时候,会遍历它的观察者,最终通过considerNotify
方法进行调用它的Observer的onChanged方法。
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
在considerNotify
中则是根据包装类型的mActive和shouldBeActive方法去判断当前观察者是否处于活跃状态。因为我们在注册的时候通过 wrapper.activeStateChanged(true)
去将包装对象的mActive标记为了true
,并且shouldBeActive
一直返回true
,也就是说,它一直满足事件分发的条件,因此每次修改value的时候,它都会收到事件。
另外,我们使用observer的时候包装类型为LifecycleBoundObserver
,它是被注册在Lifecycle中的,当Lifecycle的声明周期变化的时候会调用它的onStateChanged
,而在这个方法中当Lifecycle状态为DESTROY
的时候就会将它本身移除注册,因此不需要我们手动移除。但通过observeForeve注册的包装类型是不会注册到Lifecycle中的,也就是说,当我们不需要这个观察者的时候,必须手动通过调用removeObserver
去将他移除,否则可能会出现内存泄漏等情况。
MediatorLiveData
我们知道,使用LiveData必须要先给它注册观察者才能够接收到数据变更带来的通知,那么当我们需要观察很多数据的时候,就需要调用每个LiveData的observe进行注册。那么有没有可以只注册一个LiveData就能接收到多个LiveData的数据变更事件呢?这里就是我们要介绍的MediatorLiveData
。
MediatorLiveData是MutableLiveData的子类,它本身允许添加多个LiveData。也就是说,观察者可以通过它,实现只注册一次而能观测到多个LiveData的value变更事件。说的再多总归不如代码来的清晰,下面看一下示例代码:
// MainActivity
class MainActivity : AppCompatActivity() {
private val intLiveData = MutableLiveData<Int>()
private val stringLiveData = MutableLiveData<String>()
private val mediatorLiveData = MediatorLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mediatorLiveData.addSource(intLiveData) {
println("int liveData :$it")
}
mediatorLiveData.addSource(stringLiveData) {
println("string liveData :$it")
}
mediatorLiveData.observe(this, Observer {
println("mediator LiveData :$it")
})
thread {
intLiveData.postValue(12)
Thread.sleep(1000)
stringLiveData.postValue("string")
Thread.sleep(1000)
mediatorLiveData.postValue("mediator")
}
}
}
在上面的代码中,我们声明了三个LiveData,两个是基本的MutableLiveData,一个是MediatorLiveData。可以看到,首先通过mediatorLiveData的addSource
方法将另外两个LiveData与对应的Observer添加到MediatorLiveData,最后才注册MediatorLiveData的观察者。接着启动一个线程分别修改三个LiveData的值,打印结果如下。
2020-06-03 20:43:56.886 3744-3744/com.example.myapplication I/System.out: int liveData :12
2020-06-03 20:43:57.823 3744-3744/com.example.myapplication I/System.out: string liveData :string
2020-06-03 20:43:58.824 3744-3744/com.example.myapplication I/System.out: mediator LiveData :mediator
发现每个LiveData修改后都会触发与之绑定的Observer
,由于LiveData是被添加到MediatorLiveData中的,那么它修改后是否会触发它的Observer则是由MediatorLiveData决定的,当MediatorLiveData没有观察者或者是观察者都处于非活跃状态的时候,LiveData的事件是不会被触发的。
当然MediatorLiveData的使用情景肯定不是我们示例中的那样,示例只是为了演示MediatorLiveData如何使用,我们将通过这种最基本的使用去分析它做了什么。
//MediatorLiveData
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
// 将LiveData与Observer事件绑定
Source<S> e = new Source<>(source, onChanged);
// 加入到map集合中
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;
}
if (hasActiveObservers()) {
// 若是有活跃的观察者,则通过plug进行注册
e.plug();
}
}
// LiveData
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
在addSource
中,首先将LiveData与Observer绑定在一块然后加入到map集合中,然后hasActiveObservers
判断MediatorLiveData是否有活跃的观察者,这里的hasActiveObservers是父类LiveData的一个方法,即判断mActiveCount
是否大于0,这个参数我们在前面分析过了,会根据观察者的状态去+1
或-1
。也就是说在addSource中,当判断MediatorMLiveData有活跃的观察者的时候调用包装类的plug
方法,否则什么都不做。那么包装类Source又是什么呢?
// MediatorLiveData.Source
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);
}
}
}
包装类Source
是MediatorLiveData的一个静态内部类,并实现了Observer
,也就是说它是一个Observer事件对象。并且在onChanged事件的时候调用传递进来的观察者的onChanged方法,也就是说它转发了数据修改事件。并且在plug的时候,通过LiveData的observeForever
去注册LiveData的观察者,这种注册方式的注册前面也有说过,它不会去管观察者的生命周期状态,只要数据变化了,就会触发观察事件。
看了包装类,也就看出来了它到底是怎么做的了,首先在plug
的时候,将Source
本身注册到添加的LiveData中,然后在onChanged的时候,去转发事件给通过addSource传递的Observer
。实际上也就是相当于直接将Observer注册到LiveData中,至于加了这一层Source,则是为了加入注册和移除的功能,然后交给MediatorLiveData去控制什么时候执行。
那么继续回到MediatorLiveData,在addSource的时候,只有存在活跃的观察者时才会通过plug去注册LiveData,那么若是添加的时候没有活跃的观察者就不会注册,那么后来又是什么时候去注册的呢?
// MediatorLiveData
@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();
}
}
还记得LiveData的onActive
和onInactive
吗?我们在前面分析LiveData的时候,知道了在观察者Lifecycle状态发生变化的时候,会通知包装类LifecycleBoundObserver
的onStateChanged
方法进而调用activeStateChanged
方法。而在activeStateChanged
中,会记录当前活跃的观察者的个数,当个数从0到1
的时候,会调用onActive吗,而在个数从1到0
的时候,则会调用onInactive,但是在LiveData中,这两个方法都是一个空实现,原来是用在了这里。
MediatorLiveData重写了这两个方法,在onActive中会遍历集合去plug也就是observerForever,而在onInactive中则是遍历集合unplug也就是removeObserver。也就是说,在MediatorLiveData拥有活跃的观察者的时候,就会去注册所有通过addSource添加进来的LiveData,而所有的观察者都处于非活跃状态的时候,就会全部移除。并且这个移除只是移除LiveData的注册,而MediatorLiveData仍然会持有addSource加进来的LiveData,因此,当不需要的时候需要手动移除Source。
/ / MediatorLiveData
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}
从这里可以看出,MediatorLiveData使用的时候要慎重,万一忘记removeSouce
的话将会很容易造成内存泄露,这是因为我们的Observer基本上都是内部类,会持有Activity或Fragment,进而被MediatorLiveData持有造成内存无法释放。而我们前面的示例只是说明MediatorLiveData的使用方式,而不是正确的使用场景,因为在实际中很少这样使用。那么该如何使用MediatorLiveData呢?先看下一个要介绍的东西。
Transformations LiveData的转换器
Transformations
是LiveData包中提供的一个转换工具,它比较简单,只有两个方法:map/switchMap
。它可以将一个LiveData(A)
转换成另一个LiveData(B)
,并且B的值是根据A的改变而改变。语言比较苍白,看下面的代码示例吧:
// MainActivity
class MainActivity : AppCompatActivity() {
private val user = MutableLiveData<User>()
private val userInfo = Transformations.map(user) {
"User[${it.name},${it.age}]"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userInfo.observe(this, Observer {
println(it)
})
button.setOnClickListener {
user.value = User("张三", 12)
}
}
}
// User
data class User(var name: String, var age: Int)
在上面的代码中,我们定义了一个User类型的LiveData 常量 user
,一个通过User类型转换而来的String类型的LiveData常量userInfo
。我们在onCreate中只注册了userInfo
,并且点击按钮修改的是user的值,然而却能打印出 User[张三,12]。注意这里的userInfo是通过Transformations根据user转换而来的,此时他们是依赖关系,也就是当user的值修改后,userInfo就能收到通知,所以我们虽然没注册user,但仍然能够拿到它数据变换带来的通知。
而前面刚说完MediatorLiveData,他的作用就是可以只注册自己就可以收到其他LiveData的值发生变化时的通知,与这里是不是很像?实际上,Transformations
就是通过MediatorLiveData
实现的。
// Transformations
@MainThread
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;
}
// Function
public interface Function<I, O> {
O apply(I input);
}
Transformations的map
方法过于简陋了,就是定义一个MediatorLiveData,然后将需要map的LiveData添加进来。然后在LiveData的值变化时,通过map的第二个参数Function进行转换,转换完之后设置为MediatorLiveData的值。因此,在我们的示例中,设置新的user
的值的时候,就会将User对象转换成String,然后设置给userInfo
,从而可以收到通知。
sewitchMap
和map
是一样的,也是通过MediatorLiveData去转换的,**它和map的区别就是,map对第二个参数类型的返回值没有要求,而switchMap要求必须返回一个LiveData类型,**而这个类型也就是switchMap
的返回值类型。此时,返回值LiveData是会观测返回类型的值的修改。简单说起来就是这样:switchMap的第一个参数是用来定位的,可以根据第二个参数Function
去指向某一个LiveData
,然后返回值就就会观测到这个LiveData,能够收到它值修改的事件。下面看一下例子:
// MainActivity
private val userA = MutableLiveData<String>()
// 这个LiveData是用来确定指向的,当它的value修改后,就会去查找对应的LiveData然后返回
private val user = MutableLiveData<User>()
private val userInfo = Transformations.switchMap(user) {
// 这里直接返回userA,实际应用中应该根据user的值决定返回值
userA
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userInfo.observe(this, Observer {
println(it)
})
// 修改userA的值,绑定前不会触发userInfo的值修改,绑定后会触发
button.setOnClickListener {
userA.value = "user A"
}
// 修改user的值,此时会触发switchMap的第二个参数,然后返回userA,这时候userInfo就与userA绑定在一起了
button1.setOnClickListener {
user.value = User("张三",12)
}
}
示例中我们定义了两个LiveData,然后通过switchMap去转换user
,然后返回userA
。然后当我们先点击button的时候,虽然修改了userA
的值,但userInfo
的值并不会修改。而当我们点击了button1后,然后再去点button,这时候userInfo
的值就会与userA
的值一致,然后就会打印出user A。
在map中,我们userInfo是根据user的值来进行变化,而在switchMap中,user只是一个中转,示例中它指向了userA,此时userA的值修改后,userInfo也就会随之而变了。
// Transformations
@MainThread
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
// 将switchMap的第一个参数添加到MediatorLiveData,然后每次source的值改变的时候,就会调用下方的onChanged方法
result.addSource(source, new Observer<X>() {
// 目标LiveData,也就是Function的返回值
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 调用Function
LiveData<Y> newLiveData = switchMapFunction.apply(x);
// 这种情况是上例中,按多次button1。因为我们在Function中一直返回了userA,因此除了第一次外,其它的点击到了这一步就会返回
if (mSource == newLiveData) {
return;
}
// 若是已经有绑定的LiveData了,就移除出去。这种情况是Function返回不同的LiveData。因为我们的Function一直返回userA,所以不会到这一步
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
// source的值修改后,同步修改MediatorLiveData的值
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
代码中注释的也比较清晰了,可以看到switchMap
的第一个参数只是起到一个寻址的工具人作用,也就是每次它的值修改后,都会去寻找新的LiveData,然后帮助MediatorLiveData去与这个新的LiveData绑定。并且MediatorLiveData的值是观测
这个新的LiveData的,每次这个新LiveData值修改后,MediatorLiveData都能获得最新数据。
可以看出map与switchMap的区别:首先他们都返回一个MediatorLiveData,然后map是将这个MediatorLiveData的值与待转换LiveData相关联,每次这个LiveData值修改后,就会通过Functon去转换它的值然后赋给MediatorLiveData;而switchMap中,MediatorLiveData是与第三个LiveData关联的,待转换LiveData是个工具,当待转换LiveData的值变化后,就会通过Function去寻找第三个LiveData去返回,然后MediatorLiveData就观测第三个LiveData,每次第三个LiveData值改变后,MediatorLiveData的值也会变成相同的。
Transformations的两个功能都是通过MediatorLiveData实现的,由此也可以看出MediatorLiveData的使用情景了,主要是进行转换的,可以将一个LiveData转换成另一个LiveData。或者说他的使用场景是进行绑定,将MediatorLiveData与其他LiveData进行绑定,然后就可以监测这个LiveData。