Jetpack-LiveData 详解

1,009 阅读15分钟

LiveData介绍

作用

先来看下官方的定义:

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity/Fragment)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。

如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知。

您可以注册与实现 LifecycleOwner 接口的对象配对的观察者。有了这种关系,当相应的 Lifecycle 对象的状态变为 DESTROYED 时,便可移除此观察者。这对于 Activity 和 Fragment 特别有用,因为它们可以放心地观察 LiveData 对象,而不必担心泄露(当 Activity 和 Fragment 的生命周期被销毁时,系统会立即退订它们)。

优势

  • 确保界面符合数据状态:LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。

  • 不会发生内存泄漏:观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。

  • 不会因 Activity 停止而导致崩溃:如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

  • 不再需要手动处理生命周期:界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。

  • 数据始终保持最新状态:如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。

  • 适当的配置更改:如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。

  • 共享资源:您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。

// 以上是官方文档说明

LiveData的使用

下面介绍LiveData的使用,掌握使用方法也可以更好理解上面的内容。

基本使用

observe()方法

  1. 创建LiveData实例,指定源数据类型
  2. 创建Observer实例,实现onChanged()方法,用于接收源数据变化并刷新UI
  3. LiveData实例使用observe()方法添加观察者,并传入LifecycleOwner
  4. LiveData实例使用setValue()/postValue()更新源数据 (子线程要postValue())

举个例子:

class LiveDataTestActivity : AppCompatActivity(){
    companion object {
        private const val TAG = "LiveDataTestActivity"
    }

    var mLiveData = MutableLiveData<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mLiveData.observe(this, Observer {
            Log.i(TAG, "onChanged: " + it)
        })
        //activity是非活跃状态,不会回调onChanged。变为活跃时,value被onStart中的value覆盖
        mLiveData.value = "onCreate"
    }

    override fun onStart() {
        super.onStart()
        Log.i(TAG, "onStart: ")
        //活跃状态,会回调onChanged。并且value会覆盖onCreate、onStop中设置的value
        mLiveData.value = "onStart: "
    }

    override fun onResume() {
        super.onResume()
        Log.i(TAG, "onResume: ")
        //活跃状态,回调onChanged
        mLiveData.value = "onResume: "
    }

    override fun onPause() {
        super.onPause()
        Log.i(TAG, "onPause: ")
        //活跃状态,回调onChanged
        mLiveData.value = "onPause: "
    }

    override fun onStop() {
        super.onStop()
        Log.i(TAG, "onStop: ")
        //非活跃状态,不会回调onChanged。后面变为活跃时,value被onStart中的value覆盖
        mLiveData.value = "onStop: "
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(TAG, "onDestroy: ")
        //非活跃状态,且此时Observer已被移除,不会回调onChanged
        mLiveData.value = "onDestroy: "
    }
}

注意到 LiveData实例mLiveData的创建是使用MutableLiveData,它是LiveData的实现类,且指定了源数据的类型为String。然后创建了接口Observer的实例,实现其onChanged()方法,用于接收源数据的变化。observer和Activity一起作为参数调用mLiveData的observe()方法,表示observer开始观察mLiveData。然后Activity的所有生命周期方法中都调用了mLiveData的setValue()方法。

observeForever(Observer)方法

另外,除了使用observe()方法添加观察者,也可以使用observeForever(Observer) 方法来注册未关联 LifecycleOwner的观察者。在这种情况下,观察者会被视为始终处于活跃状态。

举个例子:

mLiveData.observeForever(Observer {
    Log.i(TAG, "observeForever onChange: " + it)
})

不过使用 observeForever 获得观察者对象会一直处于活跃状态,此时就需要我们手动调用 removeObserver(Observer) 移除该观察者。

扩展使用

扩展包括两点:

  • 自定义LiveData,本身回调方法的覆写:onActive()、onInactive()。
  • 实现LiveData为单例模式,便于在多个Activity、Fragment之间共享数据。

官方的例子如下:

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}

为了观察股票价格变动,继承LiveData自定义了StockLiveData,且为单例模式,只能通过get(String symbol)方法获取实例。 并且重写了onActive()、onInactive(),并加入了 开始观察股价更新、移除股价更新观察 的逻辑。

onActive()调用时机为:活跃的观察者(LifecycleOwner)数量从 0 变为 1 时。 onInactive()调用时机为:活跃的观察者(LifecycleOwner)数量从 1 变为 0 时。

也就是说,只有当 存在活跃的观察者(LifecycleOwner)时 才会连接到 股价更新服务 监听股价变化。使用如下:

class MyFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }

由于StockLiveData是单实例模式,那么多个LifycycleOwner(Activity、Fragment)间就可以共享数据了。

高级用法

如果希望在将 LiveData 对象分派给观察者之前对存储在其中的值进行更改,或者需要根据另一个实例的值返回不同的 LiveData 实例,可以使用LiveData中提供的Transformations类。

数据修改 - Transformations.map

//Int类型的liveData1
val liveData1 = MutableLiveData<Int>()
//转换成String类型的liveDataMap
val liveDataMap = Transformations.map(liveData1) {
    val s = "$it + Transformations.map";
    Log.i(TAG, "apply: " + s);
}
liveDataMap.observe(this, Observer {
    Log.i(TAG, "onChanged1: $it");
})
liveData1.setValue(100);

使用很简单:原本的liveData1 没有添加观察者,而是使用Transformations.map()方法 对liveData1的数据进行的修改 生成了新的liveDataMap,liveDataMap添加观察者,最后liveData1设置数据 。

此例子把 Integer类型的liveData1 修改为String类型的liveDataMap。

数据切换 - Transformations.switchMap

如果想要根据某个值 切换观察不同LiveData数据,则可以使用Transformations.switchMap()方法。

//两个liveData,由liveDataSwitch决定 返回哪个livaData数据
val liveData3 = MutableLiveData<String>()
val liveData4 = MutableLiveData<String>()

//切换条件LiveData,liveDataSwitch的value 是切换条件
val liveDataSwitch = MutableLiveData<Boolean>()

//liveDataSwitchMap由switchMap()方法生成,用于添加观察者
val liveDataSwitchMap: LiveData<String> = Transformations.switchMap(liveDataSwitch){
    if (it) liveData3 else liveData4
}

liveDataSwitchMap.observe(this,
    Observer { s -> Log.i(TAG, "onChanged2: $s") })
    
val switchValue = true
liveDataSwitch.setValue(switchValue) //设置切换条件值

liveData3.setValue("liveData3")
liveData4.setValue("liveData4")

liveData3、liveData4是两个数据源,有一个判断条件来决定 取哪一个数据 ,这个条件就是liveDataSwitch,如果值为true则取liveData3,false则取liveData4。 Transformations.switchMap()就用于实现这一逻辑,返回值liveDataSwitchMap添加观察者就可以了。

观察多个数据 - MediatorLiveData

MediatorLiveData 是 LiveData 的子类,允许合并多个 LiveData 源。只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。

val mediatorLiveData = MediatorLiveData<String>()

val liveData5 = MutableLiveData<String>();
val liveData6 = MutableLiveData<String>()

//添加 源 LiveData
mediatorLiveData.addSource(liveData5){
    Log.i(TAG, "onChanged3: $it");
    mediatorLiveData.setValue(it);
}

//添加 源 LiveData
mediatorLiveData.addSource(liveData6){
    Log.i(TAG, "onChanged4: $it");
    mediatorLiveData.setValue(it);
}

mediatorLiveData.observe(this){
    Log.i(TAG, "onChanged5: $it");
}

liveData5.setValue("liveData5");
liveData6.setValue("liveData6");

例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源:

与存储在本地数据库中的数据关联的 liveData5 与从网络访问的数据关联的 liveData6

Activity 只需观察 MediatorLiveData 对象即可从这两个源接收更新。

源码分析

前面提到 LiveData几个特点,能感知生命周期状态变化、不用手动解除观察等等,这些是如何做到的呢?

添加观察者

LiveData原理是观察者模式,下面就先从LiveData.observe()方法看起:

/**
 * 添加观察者. 事件在主线程分发. 如果LiveData已经有数据,将直接分发给observer。
 * 观察者只在LifecycleOwner活跃时接受事件,如果变为DESTROYED状态,observer自动移除。
 * 当数据在非活跃时更新,observer不会接收到。变为活跃时 将自动接收前面最新的数据。 
 * LifecycleOwner非DESTROYED状态时,LiveData持有observer和 owner的强引用,DESTROYED状态时自动移除引用。
 * @param owner    控制observer的LifecycleOwner
 * @param observer 接收事件的observer
 */
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 判断是否在主线程,如果不在,直接抛出异常
    assertMainThread("observe");
    if (owner.getLifecycle().getCurrentState() == DESTROYED) {
        // LifecycleOwner是DESTROYED状态,直接忽略
        return;
    }
    //使用LifecycleOwner、observer 组装成LifecycleBoundObserver,添加到mObservers中
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    //将 observer 作为 key,wrapper 作为 value 进行存储
    //当 mObservers 不包含该 key 时,调用 putIfAbsent 会返回 null
    //当 mObservers 已包含该 key 时,调用 putIfAbsent 不会存储 key-value,并会返回之前保存的 value
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    if (existing != null && !existing.isAttachedTo(owner)) {
    //!existing.isAttachedTo(owner)说明已经添加到mObservers中的observer指定的owner不是传进来的owner,就会报错“不能添加同一个observer却不同LifecycleOwner”
        throw new IllegalArgumentException("Cannot add the same observer"
                + " with different lifecycles");
    }
    if (existing != null) {
        return;//这里说明已经添加到mObservers中,且owner就是传进来的owner
    }
    owner.getLifecycle().addObserver(wrapper);
}

首先是判断LifecycleOwner是DESTROYED状态,就直接忽略,不能添加。接着使用LifecycleOwner、observer 组装成LifecycleBoundObserver包装实例wrapper,使用putIfAbsent方法observer-wrapper作为key-value添加到观察者列表mObservers中。(putIfAbsent意思是只有列表中没有这个observer时才会添加。) 然后对添加的结果进行判断,如果mObservers中已经存在此observer key,但value中的owner不是传进来的owner,就会报错“不能添加同一个observer却是不同LifecycleOwner”。如果是相同的owner,就直接return。 最后用LifecycleOwner的Lifecycle添加observer的封装wrapper。 另外,再看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);
}

和observe()类似,只不过 会认为观察者一直是活跃状态,

事件回调

LifecycleBoundObserver

Observer方法对应的观察者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() { 
        //只有当 Lifecycle 的当前状态是 STARTED 或者 RESUMED 时
        //才认为 Lifecycle 是处于活跃状态
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);//LifecycleOwner变成DESTROYED状态,则移除观察者
            return;
        }
        activeStateChanged(shouldBeActive());
    }

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

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

LifecycleBoundObserver是LiveData的内部类,是对原始Observer的包装,把LifecycleOwner和Observer绑定在一起。当LifecycleOwner处于活跃状态,就称 LifecycleBoundObserver是活跃的观察者。 它实现自接口LifecycleEventObserver,实现了onStateChanged方法。上一篇Lifecycle中提到onStateChanged是生命周期状态变化的回调。 在LifecycleOwner生命周期状态变化时 判断如果是DESTROYED状态,则移除观察者。LiveData自动移除观察者特点就来源于此。这就是 LiveData 可以避免内存泄露最重要的一个点,如果不是DESTROYED状态,将调用父类ObserverWrapper的activeStateChanged()方法处理 这个生命周期状态变化,shouldBeActive()的值作为参数,至少是STARTED状态为true,即活跃状态为true。

AlwaysActiveObserver

observeForever方法对应的观察者AlwaysActiveObserver,

private class AlwaysActiveObserver extends ObserverWrapper {

    AlwaysActiveObserver(Observer<? super T> observer) {
        super(observer);
    }

    @Override
    boolean shouldBeActive() {
        //使其固定返回 true,则意味着只要有数据变化就都进行数据回调
        return true;
    }
}

与LifecycleBoundObserver方法一致,它也是抽象类ObserverWrapper的实现类,其shouldBeActive()返回值固定为true,意味着只要有数据变化都会进行回调。所以使用observeForever()函数一定要在过后主动移除Observer,避免内存泄露和NPE。

ObserverWrapper
private abstract class ObserverWrapper {
    ...
    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;//活跃状态 未发生变化时,不会处理。
        }
        mActive = newActive;
        boolean wasInactive = LiveData.this.mActiveCount == 0;//没有活跃的观察者
        LiveData.this.mActiveCount += mActive ? 1 : -1;//mActive为true表示变为活跃
        if (wasInactive && mActive) {
            onActive();//活跃的观察者数量 由0变为1
        }
        if (LiveData.this.mActiveCount == 0 && !mActive) {
            onInactive(); //活跃的观察者数量 由1变为0
        }
        if (mActive) {
            dispatchingValue(this);//观察者变为活跃,就进行数据分发
        }
    }
}

ObserverWrapper也是LiveData的内部类。mActive是ObserverWrapper的属性,表示此观察者是否活跃。如果活跃状态 未发生变化时,不会处理。 LiveData.this.mActiveCount == 0 是指 LiveData 的活跃观察者数量。活跃的观察者数量 由0变为1、由1变为0 会分别调用LiveData的 onActive()、onInactive()方法。这就是前面提到的扩展使用的回调方法。 最后观察者变为活跃,就使用LiveData的dispatchingValue(observerWrapper)进行数据分发:

dispatchingValue
void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        //如果当前正处于向 mObservers 发布 mData 的过程中(即 mDispatchingValue 为 true)
        //则将 mDispatchInvalidated 置为 true,用于标明有新值到来,正在回调的值是已经过时的了
        mDispatchInvalidated = true;//如果当前正在分发,则分发无效,return
        return;
    }
    mDispatchingValue = true; //标记正在分发
    do {
        mDispatchInvalidated = false; 
        if (initiator != null) {
            considerNotify(initiator); //observerWrapper不为空,使用considerNotify()通知真正的观察者
            initiator = null;
        } else { //observerWrapper为空,遍历通知所有的观察者
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    //如果 mDispatchInvalidated 为 true,则中断继续遍历过程
                    //用新值来重新循环一遍
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false; 
}

dispatchingValue()函数设计得比较巧妙,用两个全局的布尔变量mDispatchingValue和mDispatchInvalidated就实现了新旧值判断、旧值舍弃、新值重新全局发布的逻辑。

如果当前正在分发,则分发无效;observerWrapper不为空,就使用considerNotify()通知真正的观察者,observerWrapper为空 则遍历通知所有的观察者。

其中需要注意mObservers的遍历过程,由于每遍历到一个item都会检查一次当前的value是否已经过时,是的话则中断遍历,所以是存在仅有部分Observer收到值的情况。

considerNotify
private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return; //观察者非活跃 return
    }
    //若当前observer对应owner非活跃,就会再调用activeStateChanged方法,并传入false,其内部会再次判断
    if (!observer.shouldBeActive()) {
        observer.activeStateChanged(false);
        return;
    }
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    observer.mObserver.onChanged((T) mData);//回调真正的mObserver的onChanged方法
}

先进行状态检查:观察者是非活跃就return;若当前observer对应的owner非活跃,就会再调用activeStateChanged方法,并传入false,其内部会再次判断。最后回调真正的mObserver的onChanged方法,值是LivaData的变量mData。 到这里回调逻辑也通了。

数据更新

LivaData数据更新可以使用setValue(value)、postValue(value),区别在于postValue(value)用于 子线程:

//LivaData.java

postValue
//用于在主线程对值进行回调
private final Runnable mPostValueRunnable = new Runnable() {
    @SuppressWarnings("unchecked")
    @Override
    public void run() {
        Object newValue;
        synchronized (mDataLock) {
            //通过加锁可以确保 newValue 指向的是当前最新值
            newValue = mPendingData;
            mPendingData = NOT_SET;
        }
        setValue((T) newValue); //也是走到setValue方法
    }
};

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    //如果 postTask 为 false,则说明当前有旧值需要通过 postValue 进行回调
    //因为 postValue 可以在子线程调用,而 Observer 的 onChanged(value) 方法肯定是要在主线程被调用
    //从子线程切到主线程之间是有段时间间隔的
    //等到 mPostValueRunnable 真正执行时让其直接发送最新值 mPendingData 即可,所以此处直接返回
    if (!postTask) {
        return;
    }
    //向主线程发送一个 runnable,主要是为了在子线程调用 postValue,在主线程进行值回调 ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

postValue方法把Runable对象mPostValueRunnable抛到主线程,其run方法中还是使用的setValue()

postValue(T)函数不限定调用者所在线程,不管是主线程还是子线程都可以调用,因此是存在多线程竞争的可能性的,postValue(T)函数的重点旧在于需要理解其从子线程切换到主线程之间的状态变化。

由于LiveData值回调的行为是会固定放在主线程完成的,所以postValue(T)函数将值回调的逻辑放到Runnable中再Post给Handler,最终交由主线程来执行,因此从调用postValue(T)函数到Runnable被执行之间是会有段时间差的,此时其它线程可能又调用了postValue(T)函数传递了新值。

在mPostValueRunnable被执行前,所有通过postValue(T)函数传递的value都会被保存到变量mPendingData上,且只会保留最后一个,直到mPostValueRunnable被执行后mPendingData才会被重置,所以使用 postValue(T) 函数在多线程同时调用或者单线程连续调用的情况下是存在丢值(外部的 Observer 只能接收到最新值)的可能性的。

setValue

setValue(T)函数被限定在只能主线程进行调用。

@MainThread
protected void setValue(T value) {
    // 判断是否在主线程,如果不在,就抛出异常
    assertMainThread("setValue");
    // 更新当前 value 的版本号,即 value 的新旧程度
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

setValue()把value赋值给mData,然后调用dispatchingValue(null),参数是null,对应前面提到的observerWrapper为空的场景,即 遍历所有观察者 进行分发回调。 到这里观察者模式完整的实现逻辑就梳理清晰了:LivaData通过observe()添加 与LifecycleOwner绑定的观察者;观察者变为活跃时回调最新的数据;使用setValue()、postValue()更新数据时会通知回调所有的观察者。

小结

  • 一个Observer对象只能和一个Lifecycle对象绑定,否则将抛出异常
  • 同个Observer对象不能同时使用observe()和observeForever()函数,否则将抛出异常
  • LiveData存在丢值的可能性。当单线程连续传值或者多线程同时postValue时,最终可能只有最后一个值能够被保留并回调
  • LiveData 存在仅有部分 Observer 有收到值回调的可能性。当单线程连续传值或者多线程同时传值时,假设是先后传valueA和valueB,可能只有部分Observer可以接收到valueA,然后所有Observer都接收到了valueB