LiveData是什么?
摘抄一段官方对LiveData的介绍。LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData具有生命周期感知能力
意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
本文目标
- LiveData是什么
- LiveData有什么作用
- LiveData使用方式
- LiveData原理是怎样的
LiveData有何作用
1、关注点分离
LiveData可以更好的配合MVVM架构,可以很容易的实现模型驱动界面渲染,从而使得逻辑层和界面层解耦,
2、不会发生内存泄漏
LiveData中注入了宿主的Lifecycle,可以监测界面的生命周期,可在DESTROYED状态下,反注册观察者
3、不会因 Activity 停止而导致崩溃
LiveData只有在界面可见的状态下,即STARTED或者RESUMED状态下才能收到数据。
4、不再需要手动处理生命周期
LiveData在不可见的状态下,会记录下最新的数据,但不会下发。等到界面可见时候,才会下发最新的数据。因为界面的根本是展示数据,当界面不可见的情况下,界面还有变化就没有意义了
5、适当的配置更改
如果由于配置更改而重新创建了 Activity 或 Fragment,LiveData会重新的注入新的Observer,会导致新的Observer可以收到新的数据。
6、共享资源
LiveData本质是观察者模式,存储了需要下发的数据。如果多处引用相同的LiveData,便可达到共享内存作用。
上述作用来自于官方LiveData讲解,总的来说,LiveData是一个可感知生命周期的组件,通过模型驱动的方式渲染界面。很适合状态数据的发送和接收。
LiveData原理
LiveData的结构就是一个观察者模式,所以使用可分为发送数据和监听数据两部分
一、 发送数据
setValue
protected void setValue(T value) {
// 确保在主线程中
assertMainThread("setValue");
// 版本号+1,用来判断是不是新的数据
mVersion++;
mData = value;
// 分发mData数据,
dispatchingValue(null);
}
LiveData 判断新旧数据,是通过比较version的方式,每当发送个数据时候,版本都会自增
因为都是在主线程中处理,把要发送的数据放置mData缓存即可
最终的分发都会走到dispatchingValue函数
postValue
同setValue不同,postValue可以在子线程中调用。但最终的分发流程还是会在主线程中处理。
// postValue相对于setValue,是可以在非主线程中调用的。但是会丢失中间的数据
protected void postValue(T value) {
// postTask表示上一个postValue的数据是否已经被主线程处理了,
// 如果主线程没处理,直接更新缓存中的mPendingData值即可。mPostValueRunnable中处理的时候,会使用最新的数据
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
// 如果主线程还没处理上一个数据,直接return
if (!postTask) {
return;
}
// 采用Handler处理线程切换,也保证了数据下发的顺序性
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
// 在读取mPendingData变量的时候加锁。读取该变量时候不允许再变更该变量
synchronized (mDataLock) {
newValue = mPendingData;
// mPendingData操作完成,设置成空闲状态
mPendingData = NOT_SET;
}
// 最终还会走到setValue
setValue((T) newValue);
}
};
相比较setValue,postValue要复杂许多,毕竟要涉及到多线程的处理。
postValue分为三部分
-
postValue方法中,更新内存中的数据,并判断上一个数据有没有开始处理,如果没有处理直接返回。
-
通过Handler方式,将线程切换至主线程,接下来将在主线程中执行mPostValueRunnable
-
在主线程执行mPostValueRunnable时,将待发送的数据保存至局部变量中,并将mPendingData数据置位NOT_SET,就表示该数据已经开始处理了。最终还是执行setValue((T) newValue);
如果连续的在子线程中发送数据,mPostValueRunnable没有开始处理上一个数据,具有新的数据到来,就会导致丢失中间的值。
dispatchingValue
所以最终还是回到dispatchingValue函数的执行来。
// 分发数据,当dispatchingValue参数为空时候,为全部观察者分发
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
// mDispatchingValue表示已经正在分发中,会将mDispatchInvalidated设置为true,用于拦截继续分发
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// initiator 不等空,表示对initiator该设备单独分发
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); // 当mDispatchInvalidated为true,代表有新的数据需要分发,继续循环
mDispatchingValue = false;
}
当dispatchingValue参数为空时候,为全部观察者分发。当不为空的时候,代表只处理传入的观察者即可。
对于单个的观察者的分发处理,走到了considerNotify函数中
considerNotify
@SuppressWarnings("unchecked")
private void considerNotify(ObserverWrapper observer) {
// mActive表示观察者是不是活跃的,当这个观察者被移除了或者其他原因导致的不在活跃状态
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;
}
// 通过Version来判断该数据是否对该观察者分发过,分发过则不再处理
if (observer.mLastVersion >= mVersion) {
return;
}
// 更新观察者的Version号
observer.mLastVersion = mVersion;
// 给观察者分发mData数据
observer.mObserver.onChanged((T) mData);
}
这里做了3件事
-
判断是否是活跃状态,对于不活跃的观察者,是不能都接收到新的数据,只有当回归到活跃状态下才能够接收到新的数据
-
通过版本号,判段该观察者是否已经下发了最新的数据
-
更新版本号,并下发数据源
小结: 在数据发送端
setValue必须在子线程中调用,postValue可在子线程中调用
postValue分发数据,可能会导致中间的值被丢弃
二、生命周期切换
这里是以LifecycleBoundObserver观察者举例,当我们以observer方法监听的时候,传入的观察者就会包装成LifecycleBoundObserver对象,该对象监听了宿主LifecycleOwner的生命周期,当生命周期变动,会触发数据的下发处理
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
// 默认是可见状态,即STARTED以上的状态才可能接收到新数据
@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();
// 如果宿主状态是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);
}
}
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
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);
}
}
}
对于LifecycleBoundObserver,如果要能收到新的数据,必须在STARTED或者RESUMED状态下才能。
监听状态变更的方法在onStateChanged方法中,如果当前宿主状态是DESTROYED,则可移除观察者。再循环判断两次状态不一致,则执行activeStateChanged方法。
activeStateChanged方法中,如果两次活跃状态不一致才能处理,如果是活跃状态就执行上文中的dispatchingValue方法,判断observer中数据版本不是最新的,就下发最新的数据。
三、监听数据
监听数据有两种方式,observer和observeForever。分别对应LifecycleBoundObserver、AlwaysActiveObserver这两种观察者。
AlwaysActiveObserver意味着,该观察者只要没有被移除,就一直是活跃状态,也就是当数据变更就一定能收到数据。
这里主要以LifecycleBoundObserver这种带生命感知的观察者做讲解。
// 在给定所有者的生命周期内将给定的观察者添加到观察者列表中。事件在主线程上调度。如果 LiveData 已经有数据集,它将被传递给观察者。
// 只有所有者处于Lifecycle.State.STARTED或Lifecycle.State.RESUMED状态(活动)时,观察者才会收到事件
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
// DESTROYED状态不允许添加观察者
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 包装成LifecycleBoundObserver,当STARTED或RESUMED时,观察者才会收到事件
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 该观察者已经添加过 且 两次添加的Lifecycle不一致,直接抛出异常
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
// 已经添加过,不做处理
if (existing != null) {
return;
}
// 使用宿主的Lifecycle,添加刚包装好的观察者,之后有状态变更的时候,会走到LifecycleBoundObserver中状态回调
owner.getLifecycle().addObserver(wrapper);
}
小结:
观察者不能多次注入,更不能同一个观察者绑定的生命宿主不一致,否则会抛出异常
将包装好的观察者,监听宿主的生命周期。当生命周期变更时候,也可能触发数据下发
因为LifecycleBoundObserver观察者是感知生命周期的,就可能出现这样情况。有的观察者处在活跃的状态,能收到新的数据。但有的观察者不处在活跃状态,就会导致不能的观察者收到的数据是不一样的。
从上文的分析中,可知一共有三种方式能触发观察者接收数据:
- 1、setValue等发射数据
- 2、增加观察者
observe方式
当添加新的Observer观察者,Observer中的数据版本为-1,如果添加观察之前调用setValue或者postValue设置数据,会将LiveData中最新的数据分发给观察者
observeForever
添加的观察者为AlwaysActiveObserver,不受宿主生命状态影响,如果主动移除该观察者,有新数据变更就能收到
- 3、当LiveData中的Lifecycle的生命周期发生变化的时候,可能需要分发新的数据。当观察者处在不活跃的状态下,更改了数据,LiveData只会保存,不会分发给observer方式添加的观察者。当宿主生命周期变更时候,会让观察者重新变成活跃状态,从而下发最新的数据,保证观察者中数据最新。
LiveData副作用
粘性事件:之前有通过setValue或者postValue设置过数据,当添加新的Observer会将最新的数据下发至该观察者
数据倒灌:LiveData中的数据有且只能被一个Observer消费,不能被二次的消费。该数据从业务上理解只能被消费一次的。
举个例子:当在Activity中监听了一个LiveData,正常运行后。如果配置横竖屏切换导致Activity重绘,重走生命周期,导致注入一个另一个新的观察者。又接收到了上一次的数据。导致逻辑又走了一次。对于有些业务会出现问题。
通常有下列几种解决方式
1、反射
反射更改Observer中的Version值,使其和LiveData中的版本一致,当添加新的观察者时,不会使其下发。
因为注入观察者时候,会比较observer下发的版本是不是小于缓存的数据版本,而判断有新数据,而下发新数据。
2、SingleLiveEvent
class SingleLiveEvent<T> : MutableLiveData<T?>() {
private val mPending: AtomicBoolean = AtomicBoolean(false)
@MainThread
override fun observe(owner: LifecycleOwner?, observer: Observer<T>) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
}
// Observe the internal MutableLiveData
super.observe(owner, object : Observer<T>() {
fun onChanged(@Nullable t: T) {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t)
}
}
})
}
@MainThread
override fun setValue(@Nullable t: T?) {
mPending.set(true)
super.setValue(t)
}
/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
fun call() {
value = null
}
companion object {
private const val TAG = "SingleLiveEvent"
}
}
重写LiveData方式,通过发射和下发加锁的方式,保证发送的数据,最多只能有一个观察者消费。从而解决数据倒灌问题。
3、Event包装
open class Event<out T>(private val content: T) {
var hasBeenHandled = false
private set // Allow external read but not write
/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}
/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}
通过是数据包装,在获取的之后,下次获取的数据为空保证,数据被一个观察者处理。
4、 UnPeekLiveData
KunMinX开源的一个解决此类问题的方法
public class ProtectedUnPeekLiveData<T> extends LiveData<T> {
protected boolean isAllowNullValue;
private final HashMap<Integer, Boolean> observers = new HashMap<>();
public void observeInActivity(@NonNull AppCompatActivity activity, @NonNull Observer<? super T> observer) {
LifecycleOwner owner = activity;
Integer storeId = System.identityHashCode(observer);//源码这里是activity.getViewModelStore(),是为了保证同一个ViewModel环境下"唯一可信源"
observe(storeId, owner, observer);
}
private void observe(@NonNull Integer storeId,
@NonNull LifecycleOwner owner,
@NonNull Observer<? super T> observer) {
if (observers.get(storeId) == null) {
observers.put(storeId, true);
}
super.observe(owner, t -> {
if (!observers.get(storeId)) {
observers.put(storeId, true);
if (t != null || isAllowNullValue) {
observer.onChanged(t);
}
}
});
}
@Override
protected void setValue(T value) {
if (value != null || isAllowNullValue) {
for (Map.Entry<Integer, Boolean> entry : observers.entrySet()) {
entry.setValue(false);
}
super.setValue(value);
}
}
protected void clear() {
super.setValue(null);
}
}
他是通过为每个观察者保存一个对应的标识,在分发的时候过滤掉已经发送数据的观察者。
这里通过System.identityHashCode(observer)来判断两次生成的对象是同一个对象。
LiveData相关类或方法
MediatorLiveData
合并多个LiveData,只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者
例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源: 与存储在数据库中的数据关联的 LiveData 对象。 与从网络访问的数据关联的 LiveData 对象。
public class MediatorLiveData<T> extends MutableLiveData<T> {
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
/**
* Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
* when {@code source} value was changed.
* <p>
* {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
* <p> If the given LiveData is already added as a source but with a different Observer,
* {@link IllegalArgumentException} will be thrown.
*
* @param source the {@code LiveData} to listen to
* @param onChanged The observer that will receive the events
* @param <S> The type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
// 包装成Source观察者,它是继承自Observer,通过observeForever方式监听LiveData数据
Source<S> e = new Source<>(source, onChanged);
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;
}
// 前提是该LiveData的处在活跃状态
if (hasActiveObservers()) {
// 添加对LiveData的观察
e.plug();
}
}
/**
* Stops to listen the given {@code LiveData}.
*
* @param toRemote {@code LiveData} to stop to listen
* @param <S> the type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}
@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();
}
}
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;
}
// 监听mLiveData的数据
void plug() {
mLiveData.observeForever(this);
}
// 反注册mLiveData的监听
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
// mLiveData数据有变更,会回调新注册的观察者
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
}
MediatorLiveData本质还是MutableLiveData,它是通过适配的方式,监听被监听的LiveData数据,再回调给MediatorLiveData注入的观察者
mActiveCount 表示活跃观察者的总和 AlwaysActiveObserver -- 在LiveData观察者队列就是活跃的 LifecycleBoundObserver -- 在LiveData观察者队列中,且当前的宿主是在Started、Resume状态。该观察者才能是活跃的状态
2、map
public inline fun <X, Y> LiveData<X>.map(crossinline transform: (X) -> Y): LiveData<Y> =
Transformations.map(this) { transform(it) }
public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
// 使用MediatorLiveData观察原始的LiveData,完成数据变更后在发射出去
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
LiveData的数据转变,都是基于MediatorLiveData操作的,通过监听原始的LiveData数据源,经过特殊处理后,返回新的结构
3、switchMap
public inline fun <X, Y> LiveData<X>.switchMap(
crossinline transform: (X) -> LiveData<Y>
): LiveData<Y> = Transformations.switchMap(this) { transform(it) }
@NonNull
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
// 创建一个MediatorLiveData,负责监听传入的LiveData,并返回给调用者,
// 从而保证了数据源变化,但引用不变
final MediatorLiveData<Y> result = new MediatorLiveData<>();
// 监听原始的LiveData
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
// 获取通过transform生成的LiveData
LiveData<Y> newLiveData = switchMapFunction.apply(x);
// 传入的LiveData无变化
if (mSource == newLiveData) {
return;
}
// 反订阅上一次的LiveData
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
// MediatorLiveData订阅生成的LiveData的数据源
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
// 当数据源有变化的时候,最后回调数据
result.setValue(y);
}
});
}
}
});
return result;
原始LiveData数据变更后,返回的会是一个新的LiveData,之前订阅的LiveData会被反订阅。
switchMap方法中的transform为LiveData,所以可以在switchMap中做些在主线程中不能处理的耗时工作,最终通过LiveData回调至主线程
4、DistinctUntilChanged
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
outputLiveData.addSource(source, new Observer<X>() {
boolean mFirstTime = true;
@Override
public void onChanged(X currentValue) {
final X previousValue = outputLiveData.getValue();
// 过滤逻辑,
// 第一次永远可以分发
// 和上一次不同,才会调用setValue继续分发
if (mFirstTime
|| (previousValue == null && currentValue != null)
|| (previousValue != null && !previousValue.equals(currentValue))) {
mFirstTime = false;
outputLiveData.setValue(currentValue);
}
}
});
return outputLiveData;
}
为了过滤分发相同的数据,只有和上一次的数据不相等,才不会被过滤