Jetpack之LiveData

412 阅读8分钟

官方文档:LiveData

什么是LiveData

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

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

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

LiveData的优势与劣势

优势

  • 确保界面符合数据状态

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

  • 不会发生内存泄漏

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

  • 不会因 Activity 停止而导致崩溃

    如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。

  • 不再需要手动处理生命周期

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

  • 数据始终保持最新状态

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

  • 适当的配置更改

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

  • 共享资源

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

劣势

  • postValue在异步线程可能丢失数据

    源码中新建Runnable的时候,只对mPendingData进行了修改,并不是加入线程池,导致数据丢失。

  • 对数据流的处理能力偏弱

    只提供了map、switchMap等有限的处理能力。

  • 粘性事件问题

    LiveData在注册时,会触发LifecycleOwner的activeStateChanged,触发了onChange,导致在注册前的事件也被发送出来。

粘性事件:发送消息事件早于注册事件,依然能够接收到消息的为粘滞事件。

image.png

LiveData的用法

此处实例需要配合ViewModel使用

新建一个viewModel

class ImageViewModel : ViewModel() {
	
    // define the data members the init it
    val imageUrl = MutableLiveData<String>()

    fun getImageUrl() {
        // not main thread
        val url =
            "https://image.querydata.org/movie/poster/1630459073613-611f2aa2fda7bd337df36d9a.png"
        // post value to the main thread,and then set ui accroding to this value
        imageUrl.postValue(url)
        
    }
}

在Activity中注册viewModel,此处因为依赖了implementation "androidx.fragment:fragment-ktx:1.3.6"的缘故,简化了注册方式,

private val viewModel by viewModels<ImageViewModel>()

接着,我们在Activity的onCreate方法中注册LiveData,如果观察者的生命周期处于 STARTEDRESUMED状态,获取到的数据这发送至这里,

viewModel.imageUrl.observe(this, { url ->
	// use url to do something
})

在需要获取数据的地方调用如下方法,

viewModel.getImageUrl()

至此,LiveData用法就基本掌握了。

LiveData还支持自定义,参考官方文档:监听股票价格

此外,我们也可以通过Flow的asLiveData()方法将Flow数据流转换成LiveData。

@JvmOverloads fun <T> Flow<T>.asLiveData(
    context: CoroutineContext = EmptyCoroutineContext, 
    timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T>

源码解析

首先从我们注册监听的地方入手,

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    // 判断是否在主线程,否则抛异常
    assertMainThread("observe");
    // Lifecycle状态为Destroy时,不再往下执行
    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);
}

Lifecycle的State共分为DESTROYEDINITIALIZEDCREATEDSTARTEDRESUMED,处于DESTROYED状态的生命周期对象将不会收到观察着发送的数据。

这里来看一下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) {
        Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
        // 获取当前Lifecycle状态,处于DESTROYED状态移除观察者
        if (currentState == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        Lifecycle.State prevState = null;
        // 持续监听,当状态发生改变时
        while (prevState != currentState) {
            prevState = currentState;
            activeStateChanged(shouldBeActive());
            currentState = mOwner.getLifecycle().getCurrentState();
        }
    }

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

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

此类封装了对生命周期状态的赋值与以及状态改变对观察者取消订阅的功能。

shouldBeActive()判断当前生命周期是否处于活跃状态。

看下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);
    }
}

如果活跃状态没有改变(一直都是活跃状态或者一直都是不活跃状态),终止运行,最后通过changeActiveCounter计数活跃的数量,并回调onActiveonInactive方法,最后判断如果当前处于活跃状态,分发此Observer。

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方法,首先判断是否正在分发,如果正在分发,不再往下执行。往下走,这里分为两条路线,如果观察者包装类存在,进入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);
}

这个方法依旧对观察者的活跃性做了判断,因为观察者的活跃性取决于其注册的Lifecycle,observer.mLastVersion >= mVersion用于判断是否事件被分发,如果事件被分发了,mVersion就会+1,后面会讲到,同时,会更新mLastVersion的值。最后通过onChanged回调数据给生命周期对象。

如果观察者包装类不存在,则会查找所有Observers,并分发事件出去。

接着我们来看setValue方法,postValue实际上调用的也是setValue方法,只是将数据发送到了主线程。

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

除了生命周期对象的生命周期变化之外,手动setValue也会调用dispatchingValue,这里参数传null,因为发送的事件可以到达任何一个活跃的生命周期对象那里.。

至此,将近500行代码的LiveData已经分析完成。

LiveData引起的异常

虽然LiveData简单易使用,不会造成内存泄露,并且可以自动的解绑生命周期,一定程度上保证了数据安全性。但是,在某些特殊情况下仍然会发生异常。

  • 数据倒灌

由于屏幕旋转或者用户手动更改系统语言,导致Activity重建,这次会重新注册监听LiveData事件,因此LifecycleBoundObserver会重新创建,由于其继承自ObserverWrapper,而在ObserverWrapper内部维护了一个变量mLastVersion,每个setValue或者生命周期改变,LiveData都会将其更新到其成员变量mVersion的值,而此时创建的mLastVersion初始值为-1,不满足considerNotify中如下方法:

if (observer.mLastVersion >= mVersion) {
    return;
}

因此就会继续往下走,最后重新回调onChange方法。

这里有个疑问,为什么会在屏幕旋转或者用户手动更改系统语言时发生变化呢,App被杀后台重新打开App不也会导致Activity重建吗?这里就涉及到ViewModel了,ViewModel的数据都是保存在内存在,App被杀导致整个Application被杀, ViewModel中保存的数据当然不存在了,所以在前两种情况下,ViewModel并没有销毁,这才导致了数据倒灌问题。

  • 数据丢失

当Activity重建后,页面上并没有恢复数据,这是怎么一回事呢?众所周知,除了onSaveInstanceState保存数据外,Activity页面不会保留任何数据,而如果LiveData正好写在了Activity中,并且没有做一些保存工作,那么Activity销毁时也会将LiveData一并销毁,导致无法恢复数据,所以正确的写法是将LiveData数据写在ViewModel中。

  • 事件丢失

protected void postValue(T value) {
    boolean postTask;
    synchronized (mDataLock) {
        postTask = mPendingData == NOT_SET;
        mPendingData = value;
    }
    if (!postTask) {
        return;
    }
    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。

关于LiveDataBus

可以参考美团的Android消息总线的演进之路:用LiveDataBus替代RxBus、EventBus,这篇文章主要是通过反射mLastVersionmVersion来解决数据倒灌的。