1、如何实现和生命周期的关联?
调用observe()方法时,第一个参数传入LifecycleOwner对象,而LifecycleOwner能通过getLifecycle()方法获取到lifecycle对象,然后执行lifecycle.addObserver()添加LiveData中数据(mData)变化的观察者对象。
- observe的实现逻辑:
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
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);
}
如上图所示,是使用LiveData组件实现数据更新-订阅的开发模式的层级结构。
- mData:setValue()之后更新的数据
- mVersion:在每一次调用setValue()时会进行更新
- mPending:调用postValue()时先将新的值暂存到该变量中,然后将Runnable任务post()到Handler中后,再将mPendingData
中的值赋值给mData变量,然后就是走setValue()的流程了。
2、onChange()执行时机
-
(1)onStart()之后调用setValue()立即回调onChanged(newData)
-
(2)调用observe()时,如果Observer是新建的实例,那么其绑定的mLastVersion初始值是-1,当调用lifecycleOwner.addObserver()时会调用activeStateChanged(), 然后触发dispatchingValue(), 因为不满足observe.mLastVersion>=mVersion(默认值是0,每次setValue/postValue加1),然后回调onChanged(newData)
如果是在onStop()中注册observer,那么会在回到onStart()后会回调一次onChanged()
⚠️ LiveData中判断LifecycleOwner是判断生命周期状态是否是isAtLeast(START),所以onPause时调用observe()方法也是会回调onChanged()方法的。 -
(3)LifecycleOwner生命周期变化时,还存在同因生命周期<Lifecycle.State.ON_START并没有回调onChanged(newData),则会回调一次onChanged(),把最新的mData值回调给onChanged()方法
3、说LiveData会数据倒灌是这么回事?
3.1 本质
LiveData其实本质上实现的是将事件发送时机限定在LifecycleOwner的生命周期内的粘性事件。LiveData在生命周期可见时,将不可见时更新的mData版本回传到onChanged()中。当setValue()是在observe()之前调用的,那调用observe()时会把前面setValue的最新的值传给观察者的onChanged()。
不使用LiveData进行数据更新时,一般是在UI控件变量准备好了,然后获取数据,再把数据传递给UI控件变量的某个属性,实现UI的更新,这种命令式UI的开发模式本身没有什么问题,符合日常的开发逻辑。
但是因为Android的UI架构都是基于Activity/Fragment生命周期管理的,就必然存在数据异步获取到时界面已经处于不活跃状态的情况。甚至很可能因为内存不足已经销毁了,然后在用户操作下回到活跃状态了,那么异步拿到的数据可能是没有塞给UI控件的,一般都是在onResume时又异步获取一次数据,然后更新到UI控件上。
3.2 使用LiveData实现事件注册-分发逻辑的问题
基于3.1对于LiveData数据更新本质的分析,如果我们使用LiveData的setValue/postValue,然后通过observe()分发进行监听,使用String/Int/CustomEvent(自定义的事件类,类似于EventBus)等数据做为事件分发。虽然这种方式能够避免内存泄漏,但是事件是粘性的,先发送事件,然后注册也会接收到该事件,这样的逻辑实际上并不是我们日常业务的事件逻辑。
google官方sample只处理一次的LiveData事件实现方案:
官方todoapp的Event实现
ps: 不得不说,老外的文章写得还是蛮清楚的,循序渐进,解释各种方案的问题,层层递进,对读者更好的理解有莫大帮助。
3.3 短时间内连续执行多次postValue,只有最后一次更新的值会收到数据变化的onChanged回调
这个问题是因为postValue的实现逻辑导致的,如下是postValue的代码:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
第一次postValue,postTask是true,mPendingData赋值为value,然后调用mPostValueRunnable.run方法,newValue赋值为mPendingData后,立即又将mPendingData赋值为NOT_SET了。
- 官方对postValue的备注如下:
参考文章
【1】LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)