LiveData与LiveDataBus
一、概述
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者(官方解释)。
简单来说:
LiveData可以感知自动处理我们应用组件的各个生命周期,例如当activity是为活跃(Active)状态,例如STARTED,RESUMED,才会将我们当前的数据进行分发,否则监听者将不会接受到数据的变化,避免了我们以往在子线程处理耗时行为返回数据时,当前的组件已经处于非活跃或者或者销毁状态但是没有做出安全判断处理进行ui更新处理而出现的异常奔溃现象(这句话有点长…)。
这玩意儿有什么优点呢~
1、确保界面符合数据状态(设计的出发点/作用)
LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。这样一来,您无需在每次应用数据发生变化时更新界面,因为观察者会替您完成更新。
2、不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
3、不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
4、不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
5、数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
6、适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
7、共享资源
您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。如需了解详情,请参阅扩展 LiveData。
总的来说最大的优点就是对于生命周期的把控进行数据分发,那它是怎么做到感知生命周期的呢?(摘自LiveData源码)
@MainThread
protected void setValue(T value) {
...
dispatchingValue(null);//我们先主要看这里的数据分发
}
void dispatchingValue(@Nullable ObserverWrapper initiator) {
...
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;
}
private void considerNotify(ObserverWrapper observer) {
//这里先检查当前这个数据观察者是否处于活跃状态,非活跃状态则不通知数据变化
if (!observer.mActive) {
return;
}
// 再次检查观察者是否处于即将有活跃状态的趋势,因为观察者有可能没来得及处理活跃状态这个事件(没来得及赋与状态)
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
...
observer.mObserver.onChanged((T) mData);
}
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
...
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
...
以上逻辑就是根据当前组件的状态来判断是否需要接受数据变化情况
二、使用
LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问。
public class TestViewModel extends ViewModel {
private final MutableLiveData<String> mStringLiveData = new MutableLiveData<>();
public void requestTestData() {
mStringLiveData.setValue("TOM");
}
public MutableLiveData<String> getStringLiveData() {
return mStringLiveData;
}
}
public class LivedataActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TestViewModel testViewModel = new ViewModelProvider(this).get(TestViewModel.class);
testViewModel.getStringLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(String data) {
Log.d("Monitor_data_changes", data);
}
});
testViewModel.requestTestData();
}
}
这样一来,只要我们调用testViewModel.requestTestData(),就能接收到Log.d(“Monitor_data_changes”, data) 打印的日志变化了,这里requestTestData只是简单的数据分发,日常开发中这里通常是各种网络请求或者复杂的算法逻辑(耗时操作)。
- 根据这个特性,在我们开发过程中肯定会使用Fragment来实现不同的页面,那么我们就能利用在同一个Activity共享ViewModel,利用LiveData注册观察者从而实现各个Fragment之间进行通信了。
接下来问题来了:
大家有没有在以往面试的时候被面试官问到,假如我使用Livedata连续调用了多次setValue
,例如setValue(1)、setValue(2)、setValue(3),那我会接收到几个数据呢?
那面试官为什么要这么问呢,不是三个吗,这有什么难的,不过再想想…万一答错了呢…
我猜面试官可能想引导问你setValue跟postValue两者的区别,看你是不是对它是否足够了解。
那两者有什么特殊的区别吗?不就是一个在子线程调用(非UI线程)一个在主线程(UI线程)调用吗?
雀氏,setValue是在主线程使用的,postValue可以在子线程调用,看看源码:
@MainThread//这个注解已经表明需要在UI线程进行调用了
protected void setValue(T value) {
assertMainThread("setValue");//假如我在子线程调用,这里会直接抛出异常,看看下面源码
mVersion++;
mData = value;
dispatchingValue(null);
}
...
static void assertMainThread(String methodName) {
if (!ArchTaskExecutor.getInstance().isMainThread()) {
throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
+ " thread");
}
}
那么回到前面的问题,看了源码雀氏setValue之后会接受到三次数据分发(通知),那么postValue是不是也是三次呢,大家有没有遇到过一个奇怪的bug,就是当我在连续使用Livadata#postValue的时候,观察者只接受到一个数据,那到底postValue是只接受到一次数据还是代码哪里出了呢?我们看看postValue的源码:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {//既然子线程可使用,那就会涉及到线程安全问题,这里使用了线程对象锁
postTask = mPendingData == NOT_SET;//判断本次操作是否设置过意图数据,分发完数据后将会置为NOT_SET
mPendingData = value;//意图数据,也就是你想到分发的数据
}
if (!postTask) {//连续调用的话,第一次将一定是true,第二次将变成false因为mPendingData已经被赋值了
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);//这里实现线程切换
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
//由于线程切换需要一定的时间,在切换线程的时间空隙中,假如连续postValue将会导致mPendingData 会被后面新的数据覆盖,这样的话当线程切换完成之后,分发的数据将会是最后一次postValue的值
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
那也就能解释为什么我在连续几次postValue怎么就只监听到一次数据回调,但是也不是一定只有一次,那就要看线程切换时间的耗时了:
//这里简单new一个线程偷个懒
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {//这里暴力一点循环刷新数据10000000次
liveData.postValue(i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000000; i++) {
liveData.postValue(i);
}
}
}).start();
......
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9996623
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9997132
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9997641
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9997894
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9998147
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9998656
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9999165
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9999674
2022-07-11 18:40:54.528 16057-16057/com.example.myapplication D/测试数据: 9999999
平时我们顶多就多post几个数据,时间小到可以细微到忽略不计,所以就只能接收到最后一个值了。那也许有些小伙伴会有疑问,那这么明显的 “Bug” google官方难道会不知道吗?我个人理解,Livedata最原始的设计初衷只是用来更新页面状态,并不是设计用作来传递“事件”的,页面最终展示的状态也就是我们上述连续post的最后一个数据值,那中间获取到的那些数据是没有必要展示的,也就没有必要接收,那只接收到最后一个也没错,有其他见解的小伙伴可以评论一起探讨。
我们从mPostValueRunnable 可以看到postValue实际也是调用setValue,那我们就来分析Livedata是如何分发数据的呢。
三、setValue
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
1.这里我们会看到有一个mVersion这个变量,那它是用来干什么的呢?我们来找一找它在源码的几处主要使用~
/**
* Creates a LiveData with no value assigned to it.
*/
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
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);
}
上述这几处可以看出,实际上mVersion是一个标志位,因为LiveData在数据分发的时候会根据Version比对判断版本是否是已“发布”过从而判断是否进行数据的分发,每setValue一次就增加一次版本,因为不能因为LiveData分发一次数据而导致Observer接收到多次。
2.mData就是我们想要传递分发的值
3.接下来就是分发数据dispatchingValue:
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;
}
这里有一个initiator对象,它其实就是我们设置的观察者对象,我们看setValue的时候调用dispatchingValue(null),这个时候就是遍历分发一次所有的观察者保证所有的观察者都能接收到数据,那什么时候不为空呢?
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);//这里调用参数不为空
}
}
}
这块是所有观察者的父类,它帮我们处理了对生命周期状态的监听,当状态发生改变并且观察者所附属的组件为激活状态时(RESUMED or STARTED),会对每个观察者单独分发一次事件,让UI界面的数据保持正确性,这里有什么补充或者见解的欢迎各位补充说明。
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);//最终数据的分发就到这里结束,我们设置的观察者也就在onChanged获取到需要更新的数据了
}
讲到这里,Livedata得到基本设计也就讲完了。
那么相信大家多多少少看过或者使用过LivedataBus,忘了是哪一位大佬先想出来的,后面就出现了各种版本的修改版,但是基本都差不多,思路是一样的,万变不离其宗
四、LivedataBus
相信很多人都是用LivedataBus来替代EventBus、RxBus,讲到这里我们要先说下“粘性事件”跟“数据倒灌”
- 粘性事件
那什么是粘性事件呢?简单来说,就是先发送事件,后观察者再订阅,可以接收到事件 - 数据倒灌
那数据倒灌呢?按照我的理解,通常情况下出现的场景是旋转屏幕或者切换系统语言,当前的Livedata实例基本存在于ViewModel中并未被销毁,源码中值Livedata实例只保留最后一个数据对象,就是上文的mData,利用上文例子:
public class LivedataActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TestViewModel testViewModel = new ViewModelProvider(this).get(TestViewModel.class);
testViewModel.getStringLiveData().observe(this, new Observer<String>() {
@Override
public void onChanged(String data) {
Log.d("Monitor_data_changes", data);
}
});
...
}
}
@MainThread
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);
}
观察者将会重新实例化,使用匿名内部类将在内部实例一个LifecycleBoundObserver观察者对象,而所有的观察者都继承ObserverWrapper,它内部的mLastVersion 将重新被赋值变成 -1,根据数据分发条件:
...
if (observer.mLastVersion >= mVersion) {
return;
}
...
mLastVersion重新初始化变成了-1,肯定比当前的mVersion小,则会进行数据分发,那么老的数据将会分发给重新初始化的观察者,这就是数据倒灌。那为什么observe订阅观察者为什么会触发事件分发呢?接着往下看
@Override
public void addObserver(@NonNull LifecycleObserver observer) {
enforceMainThreadIfNeeded("addObserver");
State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
if (previous != null) {
return;
}
LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
if (lifecycleOwner == null) {
// it is null we should be destroyed. Fallback quickly
return;
}
boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
State targetState = calculateTargetState(observer);
mAddingObserverCounter++;
while ((statefulObserver.mState.compareTo(targetState) < 0
&& mObserverMap.contains(observer))) {
pushParentState(statefulObserver.mState);
final Event event = Event.upFrom(statefulObserver.mState);
if (event == null) {
throw new IllegalStateException("no event up from " + statefulObserver.mState);
}
statefulObserver.dispatchEvent(lifecycleOwner, event);//关键是这里,当判断当前状态不一致则表示状态改变,那将会对新的组件状态进行分发
...
}
...
}
static class ObserverWithState {
State mState;
LifecycleEventObserver mLifecycleObserver;
ObserverWithState(LifecycleObserver observer, State initialState) {
mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
mState = initialState;
}
void dispatchEvent(LifecycleOwner owner, Event event) {
State newState = event.getTargetState();
mState = min(mState, newState);
mLifecycleObserver.onStateChanged(owner, event);//呐,又回到了上文的观察者状态改变的逻辑,当前状态为激活状态时将对数据进行分发
mState = newState;
}
}
讲到这里基本解释完了,这两个问题也是常规逻辑封装LiveDataBus众所周知的一个问题,那google官方在设计Livedata不知道有这么多bug吗?按我的理解,goodle设计livedata的时候也不是给我们分发事件来用的,只是用来给我们在UI线程也好子线程也好能够方便的分发数据进行界面状态更新。那为了解决上述这两种问题,相信网上有挺多示例,我这里就贴一下~
1、我们上述讲到问题的根源在于mVersion的值,那我们可以干涉mVersion来处理:
private void hook(@NonNull Observer<T> observer) throws Exception {
//get wrapper's version
Class<LiveData> classLiveData = LiveData.class;
Field fieldObservers = classLiveData.getDeclaredField("mObservers");
fieldObservers.setAccessible(true);
Object objectObservers = fieldObservers.get(this);
Class<?> classObservers = objectObservers.getClass();
Method methodGet = classObservers.getDeclaredMethod("get", Object.class);
methodGet.setAccessible(true);
Object objectWrapperEntry = methodGet.invoke(objectObservers, observer);
Object objectWrapper = null;
if (objectWrapperEntry instanceof Map.Entry) {
objectWrapper = ((Map.Entry) objectWrapperEntry).getValue();
}
if (objectWrapper == null) {
throw new NullPointerException("Wrapper can not be bull!");
}
Class<?> classObserverWrapper = objectWrapper.getClass().getSuperclass();
Field fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion");
fieldLastVersion.setAccessible(true);
//get livedata's version
Field fieldVersion = classLiveData.getDeclaredField("mVersion");
fieldVersion.setAccessible(true);
Object objectVersion = fieldVersion.get(this);
//set wrapper's version
fieldLastVersion.set(objectWrapper, objectVersion);
}
}
或者说可以自己维护一个版本号(摘自: https://github.com/KunMinX):
public class ProtectedUnPeekLiveData<T> extends LiveData<T> {
public ProtectedUnPeekLiveData(T value) {
super(value);
}
public ProtectedUnPeekLiveData() {
super();
}
private final static int START_VERSION = -1;
private final AtomicInteger mCurrentVersion = new AtomicInteger(START_VERSION);
protected boolean isAllowNullValue;
/**
* TODO 当 liveData 用作 event 时,可使用该方法观察 "生命周期敏感" 非粘性消息
* <p>
* state 可变且私有,event 只读且公有,
* state 倒灌应景,event 倒灌不符预期,
* <p>
* 如这么说无体会,详见《吃透 LiveData 本质,享用可靠消息鉴权机制》解析:
* https://xiaozhuanlan.com/topic/6017825943
*
* @param owner activity 传入 this,fragment 建议传入 getViewLifecycleOwner
* @param observer observer
*/
@Override
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
super.observe(owner, createObserverWrapper(observer, mCurrentVersion.get()));
}
/**
* TODO 当 liveData 用作 event 时,可使用该方法观察 "生命周期不敏感" 非粘性消息
*
* @param observer observer
*/
@Override
public void observeForever(@NonNull Observer<? super T> observer) {
super.observeForever(createObserverWrapper(observer, mCurrentVersion.get()));
}
/**
* TODO 当 liveData 用作 state 时,可使用该方法来观察 "生命周期敏感" 粘性消息
*
* @param owner activity 传入 this,fragment 建议传入 getViewLifecycleOwner
* @param observer observer
*/
public void observeSticky(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
super.observe(owner, createObserverWrapper(observer, START_VERSION));
}
/**
* TODO 当 liveData 用作 state 时,可使用该方法来观察 "生命周期不敏感" 粘性消息
*
* @param observer observer
*/
public void observeStickyForever(@NonNull Observer<? super T> observer) {
super.observeForever(createObserverWrapper(observer, START_VERSION));
}
/**
* TODO tip:只需重写 setValue
* postValue 最终还是会经过这里
*
* @param value value
*/
@Override
protected void setValue(T value) {
mCurrentVersion.getAndIncrement();
super.setValue(value);
}
/**
* TODO tip:
* 1.添加一个包装类,自己维护一个版本号判断,用于无需 map 帮助也能逐一判断消费情况
* 2.重写 equals 方法和 hashCode,在用于手动 removeObserver 时,忽略版本号的变化引起的变化
*/
class ObserverWrapper implements Observer<T> {
private final Observer<? super T> mObserver;
private int mVersion = START_VERSION;
public ObserverWrapper(@NonNull Observer<? super T> observer, int version) {
this.mObserver = observer;
this.mVersion = version;
}
@Override
public void onChanged(T t) {
if (mCurrentVersion.get() > mVersion && (t != null || isAllowNullValue)) {
mObserver.onChanged(t);
}
}
@SuppressWarnings("unchecked")
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ObserverWrapper that = (ObserverWrapper) o;
return Objects.equals(mObserver, that.mObserver);
}
@Override
public int hashCode() {
return Objects.hash(mObserver);
}
}
/**
* TODO tip:
* 通过 ObserveForever Observe,需记得 remove,不然存在 LiveData 内存泄漏隐患,
* 保险做法是,在页面 onDestroy 环节安排 removeObserver 代码,
* 具体可参见 app module ObserveForeverFragment 案例
*
* @param observer observeForever 注册的 observer,或 observe 注册的 observerWrapper
*/
@Override
public void removeObserver(@NonNull Observer<? super T> observer) {
if (observer.getClass().isAssignableFrom(ObserverWrapper.class)) {
super.removeObserver(observer);
} else {
super.removeObserver(createObserverWrapper(observer, START_VERSION));
}
}
private ObserverWrapper createObserverWrapper(@NonNull Observer<? super T> observer, int version) {
return new ObserverWrapper(observer, version);
}
/**
* TODO tip:
* 手动将消息从内存中清空,
* 以免无用消息随着 SharedViewModel 长时间驻留而导致内存溢出发生。
*/
public void clear() {
super.setValue(null);
}
}
这是两种比较主流的解决方案,还有其他的一些解决方法就不一一赘述了,但是解决思路是一样的,只要你理解了Livedata是如何设计的,相信你怎么改它都是Ok的!
总结
不管是LiveDataBus也好,EventBus、RxBus也好,都是为了分发事件解决通信问题,也没有孰好孰坏,是要场景合适用哪个都一样,只要不出bug就行(个人观点,有其他见解可以评论区讨论哈)…写文章不易,希望各位小伙伴点个赞鼓励下~