上篇我们介绍了Lifecyle,这篇继续了解一下Jetpack系列之二:Livedata。

1.什么是LiveData?

LiveData是一种类,持有可被观察的数据。LiveData是一种可感知生命周期的组件,它是基于lifecycle组件的,意味着该组件重视其他app组件的生命周期,如Activity、Fragment、Service。该组件能确保,仅仅在Activity\Fragment\Service等组件都处于活跃的生命周期状态的时候,才去更新app组件。Activity、Fragment不用担心会出现内存泄露,在Activity、Fragment销毁时,LiveData会自动解除其注册关系。

2.为什么要用Livedata?

  1. LiveData能确保UI和数据状态相符

    因为是观察者模式,LiveData会在生命周期状态改变时,通知观察者
    可以在观察者对象中进行UI的更新操作

  2. LiveData没有内存泄露

    观察者和Lifecycle对象绑定,能在销毁时自动解除注册

  3. LiveData不会给已经停止的Activity发送事件

    如果观察者处于非活跃状态,LiveData不会再发送任何事件给这些Observer对象

  4. LiveData能确保不再需要手工对生命周期进行处理

    UI组件仅仅需要对相关数据进行观察
    LiveData自动处理生命周期状态改变后,需要处理的代码。

  5. LiveData能保证数据最新

    一个非活跃的组件进入到活跃状态后,会立即获取到最新的数据
    不用担心数据问题

  6. LiveData在横竖屏切换等Configuration改变时,也能保证获取到最新数据

    例如Acitivty、Fragment因为屏幕选装导致重建, 能立即接收到最新的数据

  7. LiveData能资源共享

    如果将LiveData对象扩充,用单例模式将系统服务进行包裹。这些服务就可以在app中共享。
    只需要LiveData和系统服务connect,其他观察者只需要监视LiveData就能获取到这些资源

3.简单使用

首先在你的模块build.gradle里面添加依赖:

dependencies {
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"
}

最简单的一种使用是MutableLivedata:

class MainActivity : AppCompatActivity() {

private val mLivedata = MutableLiveData<String>()//1
private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
mLivedata.observe(this) {//2
mBinding.textView.text = it
}
var num = 0
mBinding.button.setOnClickListener {
mLivedata.value = num++.toString()//3
}
}
}

我这里用了viewbinding代替了findviewbyid。首先在1处定义了mLivedata,类型是MutableLiveData,MutableLiveData是LiveData的子类,暴露出了setValue和postValue两个方法。然后在2处订阅,传入的第一个参数是拥有生命周期的组件,一般是Activity或者Fragment或者是fragment里面的veiw,第二个参数是收到数据之后的回调,这里就是用一个textVeiw将值呈现了出来。然后在3处给一个按钮设置监听,每按一次就发送一个数据。大概的用法还是比较简单的。

再看几个比较进阶的用法:

如果我们想要在LiveData对象分发给观察者之前对其中存储的值进行更改,可以使用Transformations.map()和Transformations.switchMap()

map()是在数据分发之前进行一些处理,而switchMap更多是在多条LiveData下选择一条LiveData:

class MainActivity : AppCompatActivity() {

private val mLivedata = MutableLiveData<Int>()
private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
mLivedata.observe(this) {
mBinding.textView.text = it.toString()
}
var num = 0
mBinding.button.setOnClickListener {
mLivedata.value = num++
}
Transformations.map(mLivedata) {//1
return@map it - 10000
}.observe(this) {
Toast.makeText(this, it.toString(), Toast.LENGTH_SHORT).show()
}
Transformations.switchMap(mLivedata) {//2
val mutableLiveData1 = MutableLiveData<String>()
val mutableLiveData2 = MutableLiveData<String>()
mutableLiveData1.value = (it + 10).toString()
mutableLiveData2.value = (it - 10).toString()
if (it < 10) return@switchMap mutableLiveData1
else return@switchMap mutableLiveData2
}.observe(this) {
mBinding.textView2.text = it
}
}
}

用法很简单,在1处使用Transformations.map对值进行处理,这里就是对原值减10000,然后observer新的LiveData,这里就是弹一个吐司。在2处Transformations.switchMap(mLivedata)对原值进行判断,大于10就返回mutableLiveData1,否则mutableLiveData2,然后对新的LiveDataObserver显示在TextView上面。

这两种用法不是很常用。还有一种用法,当有多条LiveData时,我们需要合并成一条LiveData,这时候可以用MediatorLiveData:

class MainActivity : AppCompatActivity() {

private val mediatorLiveData = MediatorLiveData<Int>()
private val mutableLiveData1=MutableLiveData<Int>()
private val mutableLiveData2 = MutableLiveData<Int>()


private val mBinding: ActivityMainBinding by lazy { ActivityMainBinding.inflate(layoutInflater) }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(mBinding.root)
var num = 0
mBinding.button.setOnClickListener {
mutableLiveData1.value = num+20
}
mBinding.button2.setOnClickListener {
mutableLiveData2.value = num-20
}
mediatorLiveData.addSource(mutableLiveData1){
mediatorLiveData.value = it
}
mediatorLiveData.addSource(mutableLiveData2){
mediatorLiveData.value = it
}
mediatorLiveData.observe(this){
mBinding.textView.text = it.toString()
}
}
}

首先我们创建了两条MutableLiveData,然后定义了一个MediatorLiveData,用了两个按钮,分别发送LiveData1和LiveData2的值,再将他们加入到MediatorLiveData,MediatorLiveData本身也是LiveData,再对其Observer这样就达到合并多条LiveData的值。到这里LiveData的一些简单用法就是这些了。那大家有没有疑问,Activity是怎么感知LiveData发送了值的?LiveData为什么不会造成内存泄漏?我们来看看它的源码,把它扒个精光。

4.原理

首先我们看LiveData的源码和看LifeCycle的源码一样,从我们写的代码入手,那就是Observer:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {//1
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);//2
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);//3
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);//4
}

被注解在主线程运行,1处的条件判断说明我们在Destroy去Observer是没用的,2处把我们传进来的owner和observer封装成了LifecycleBoundObserver,它的作用我们等会分析,3处把wrapper添加进了一个map里面,说明我们的liveData是可以有多处Observer的,之后是一些安全判断,最后在4处将我们的wrapper注册到lifeCycle,这不就是我们lifeCycle里面学的嘛,那这个LifecycleBoundObserver肯定实现了LifeCycleObserver我们看一看:

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) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();//1
if (currentState == DESTROYED) {//2
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {//3
prevState = currentState;
activeStateChanged(shouldBeActive());//4
currentState = mOwner.getLifecycle().getCurrentState();
}
}

@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}

@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}

果然,实现了LifecycleEventObserver来感知我们的activity生命周期,那肯定在生命周期回调的方法onStateChanged里面做了文章,我们看,在1处获取了activtiy当前的生命周期状态,2处,处于destroy的话就移除Observer,这说明了当我 们的activtiy销毁的时候livedata会自动解除监听,所以才不会造成内存泄漏。然后在3处判断如果当前状态和即将发生的状态不一样就执行activeStateChanged(shouldBeActive()),那我们看看shouldBeActive:

@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
public enum State {
/**
* Destroyed state for a LifecycleOwner. After this event, this Lifecycle will not dispatch
* any more events. For instance, for an {@link android.app.Activity}, this state is reached
* <b>right before</b> Activity's {@link android.app.Activity#onDestroy() onDestroy} call.
*/
DESTROYED,

/**
* Initialized state for a LifecycleOwner. For an {@link android.app.Activity}, this is
* the state when it is constructed but has not received
* {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} yet.
*/
INITIALIZED,

/**
* Created state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
* <ul>
* <li>after {@link android.app.Activity#onCreate(android.os.Bundle) onCreate} call;
* <li><b>right before</b> {@link android.app.Activity#onStop() onStop} call.
* </ul>
*/
CREATED,

/**
* Started state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached in two cases:
* <ul>
* <li>after {@link android.app.Activity#onStart() onStart} call;
* <li><b>right before</b> {@link android.app.Activity#onPause() onPause} call.
* </ul>
*/
STARTED,

/**
* Resumed state for a LifecycleOwner. For an {@link android.app.Activity}, this state
* is reached after {@link android.app.Activity#onResume() onResume} is called.
*/
RESUMED;

/**
* Compares if this State is greater or equal to the given {@code state}.
*
* @param state State to compare with
* @return true if this State is greater or equal to the given {@code state}
*/
public boolean isAtLeast(@NonNull State state) {
return compareTo(state) >= 0;
}
}

就是将当前状态和START状态相减,大于等于0为true,那也就是START和RSUME这两个状态,LiveData才有效,这也符合用户使用习惯,activtiy也是在START和RSUME呈现页面。

再来看看activeStateChanged():

void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}

首先判断mActive是否和newActive相等,mActive初始状态是不活越的,为flase,所以在newActive为false时也就是activty为不活越的状态就会直接返回而不会直接分发值,之后如果是活跃的状态就changeActiveCounter(mActive ? 1 : -1);改变activeCounter的值,这个我们用不到,不用管,主要是dispatchingValue,如果是活跃状态就分发值,并且将当前的this传进去,我们看看:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {//1
considerNotify(initiator);
initiator = null;
} else {//2
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}

主要操作在下面的do while循环里面,判断我们传进来的ObserverWrapper是否为空,不为空就执行considerNotify(initiator);为空就循环遍历我们一开始observer里面添加的observerWrapper,为什么是for循环?因为我们刚才说了,一个liveData可以被多个生命周期组件观测。所以在这里为null的时候从Observers这个map里面去拿。我们看看considerNotify(initiator);

private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {//1
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {//2
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {//3
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}

在1处判断,不活越就直接结束。在2处是一些边界处理,上面的注释也说了当activty生命周期改变时,还未来的及改变mActive的值,此时应该先更改活跃的状态再分发值,这其实不重要我们一般碰不到。然后在3处判断上次的version是否比当前的mVersion大,否则再在下面更新mLastVersion,并且执行onChange,也就是我们再observer方法里面传进来的lamda表达式。一开始mLastVersion和mVersion都是为-1的,所以不会执行下面的回调。到这里Observer方法我们从始至终理解了一边。那一开始我们的onChanged不会回调,那什么时候会呢?答案在setValue里面,我们看一看:

@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}

注解说明,setValue是在主线程执行,然后mVersion++;因为一开始mVersion是等于mLastVersion的,现在++了,肯定比mLastVersion大,之后dispatchingValue(null),注意看,这时候传入的值为null,也就是我们刚刚说的dispatchingValue传入值为null的时候会for遍历所有的Oberver然后considerNotify,也就到了:

private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}

这时候mVsersion小于mLastVersion,所以就是会执行onChanged,也就是我们传进来的lamda表达式,这就说清楚了为什么每次setValue,activity都能收到值得原因啦。那我们再看看liveData的另外一个方法,postValue:

protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;//1
mPendingData = value;//2
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);//3
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);//4
}
};

postValue是用来在子线程向liveData发送值的,首先判断mPendingData的值是不是还没设置过,是的话才会分发值,并且将value赋给mPendingData,然后将mPostValueRunnable扔到主线程运行,mPostValueRunnable里面把mPendingData赋值给newValue,再将mPendingData置为未设置,最后执行的就是setValue;其实postToMainThread也就是用Handler发送一个消息到主线程:

@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = createAsync(Looper.getMainLooper());
}
}
}
//noinspection ConstantConditions
mMainHandler.post(runnable);
}

那你们有没有发现一个问题,既然setValue是用Handler发一个消息到主线程,而Handler消息是个队列,那假如我同时postValue多次,但由于前面的消息还没处理完,mPostValueRunnable还没有运行,mPendingData还没有被置为NOT_SET,postTask就一直为false,所以后面的postValue直接return了,造成我中间postValue的值直接被覆盖了,从而只收到了最新postValue的值,造成了中间值丢失的问题。解决问题很简单,自己切换到主线程然后用setValue,setValue是不会丢值得。

至此,LiveData的源码分析的差不多了,至于liveData的其他什么方法比如说,observerForever,rmoveobserverForever,都比较简单了,而且也不常用,相信你把这些基础的搞懂,其他的不会太难理解。

LiveData数据倒灌

数据倒灌简而言之就是我们并没有给livedata设置数据而收到了旧的数据。

LiveData的数据倒灌问题。就是当我们用LiveData去发送数据发生事件的时候,你会发现事件会多次发生。我们知道我们的LiveData是在onCreate去Observe的,并且通过源码我们知道,Observer的时候内部会new 一个LifecycleBoundObserver,并且将observe.mLastVersion置为默认值-1,但是此时LiveData的mVersion是没有改变的。而我们的LiveData一般是放在viewmoodel中的,liveData肯定比Activity活的时间长,假如我们在onCreate里去Observe并且给LiveData发送数据,在此过后mVersion是++了的也就是说mVersion至少是大于初始值-1的,假如此时Activtiy重建,重走onCreate方法,此时mVersion是保存在LiveData里面,LiveData是在viewmodel里面,LiveData的实例没有被重建,但是重新走了onCreate,重新Observe了,又重新new了一个LifecycleBoundObserver,也就是mLastVersion为-1,所以此时mLastVersion<mVersion,会回调我们的onChanged()。这就是为什么用LiveData发送事件会被消费多次。而数据多贴一遍我们也不会觉得有问题。但是这其实也不是LiveData的一个bug,liveData设计理念就是专注于给UI层发送数据,对于事件这种我们更应该使用flow或者rxjava。但是把LiveData改改也能用:

class SingleLiveData<T> : MutableLiveData<T>() {
private val mPending = AtomicBoolean(false)

@MainThread
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
if (hasActiveObservers()) {
Log.w(
"SingleLiveEvent",
"Multiple observers registered but only one will be notified of changes."
)
}
super.observe(owner) { t ->
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
}

@MainThread
override fun setValue(t: T?) {
mPending.set(true)
super.setValue(t)
}

@MainThread
fun call() {
value = null

}
}

AtomicBoolean是通过原子方式更新 boolean 值,能够保证线程安全。这也是官方Demo里面的解决方案,用个boolean记录一次value是否被消费。一次setValue对应一次onChanged并且这个Boolean值是保存在LiveData中的,这样就能用于发送事件了。