理解结论
定义
本质是一种具有声明周期感知能力的可观察的数据存储器类
优势
- 确保页面符合状态
- 不会发生内存泄漏
- 不会因Activity停止而崩溃
- 不需要手动处理生命周期
- 数据始终保持最新态
- 适应配置更改,因配置更改而重建的Activity会拿到最新的数据
- 共享资源,单利LiveData使用场景下
活跃态
STARTED和RESUMED认为是活跃态
如何使用
基本使用
//创建LiveData对象,一般数据层会在ViewModel中声明 private val testLiveData: MutableLiveData
= MutableLiveData() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) //注册观察者,一般在onCreate里 testLiveData.observe(this){value-> Log.i("TempActivity",value.toString()) } //主线程改变值 testLiveData.value = 1 lifecycleScope.launch(Dispatchers.IO){ delay(3000) //子线程改变值 testLiveData.postValue(2) } } 组合使用
- LiveData 结合单利
- LiveData + Room
- LiveData + 协程
扩展使用
- 活跃观察者监听,这个配合单利LiveData,可以完成当有观察者时开启某个服务,没有时取消该服务的操作,示例如下:
class StockLiveData(symbol: String) : LiveData
() { private val stockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } //当有活跃的观察者时开启服务,去跟新数据 override fun onActive() { stockManager.requestPriceUpdates(listener) } //当没有活跃观察者时停止服务减少资源消耗 override fun onInactive() { stockManager.removeUpdates(listener) } }
- 使用Transformations 类转换LiveData,类似于RxJava的map和switchMap;应用与在数据分发给观察者时的改变(通过
Transformations.map()
方法实现)和根据一个数据返回不同LiveData实例(通过Transformations.switchMap()
方法实现)的场景。- 合并多个LiveData,通过MediatorLiveData实现
关键源码
类图
关键方法
创建对象,注意有两个,但并不是重载构造方法
public LiveData(T value) { mData = value; //这里会对mVersion+1 mVersion = START_VERSION + 1; } public LiveData() { mData = NOT_SET; //这里不会对mVersion+1 mVersion = START_VERSION; }
设置值setValue和postValue
protected void setValue(T value) { assertMainThread("setValue"); //先加版本号 mVersion++; //直接赋值,这里并没有做数据是否相同的校验, //也就是说及时设置的是同一个值,也会改版本号,并执行dispatchingValue逻辑 mData = value; //发布数据变化 dispatchingValue(null); } protected void postValue(T value) { //是否发送任务 boolean postTask; //对象锁,这种思路我们可以借鉴下,多线程改一个值如何保证其安全统一 synchronized (mDataLock) { //预值是否为初始值,如果是,说明可以改变 postTask = mPendingData == NOT_SET; //将值给预值 mPendingData = value; } //如果预值已经不是初始值了,说明上次修改的还没恢复回来,则不能post任务 if (!postTask) { return; } //满足修改条件,异步去改变任务,这里从方法字面看就是将修改只的操作切到主线程 //从这里看一看出这是在子线程用的方法,以确保最后修改值后的事件通知能到主线程 ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); } //异步任务对象定义 private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { //新值定义 Object newValue; //对象锁锁住赋值操作 synchronized (mDataLock) { //将post是改了的预值给新值 newValue = mPendingData; //将预值恢复为未设置,这里和postValue的逻辑呼应了,确保了线程安全 mPendingData = NOT_SET; } //调用setValue修改mData及mVersion并分发变动 setValue((T) newValue); } };
分发数据变化的逻辑
void dispatchingValue(@Nullable ObserverWrapper initiator) { //如果正在分发数据变化 if (mDispatchingValue) { //则修改分发无效变量为true mDispatchInvalidated = true; //停止分发 return; } //修改正在分发标识为true,标志着进入分发中,分发未完成前不会重复分发 mDispatchingValue = true; do { //分发无效标识为false,说明正在进行有效分发,从整体看只有重复分发才会改为true也就是说正常分发一次就会直接结束循环 mDispatchInvalidated = false; //这是观察装饰者,如果观察装饰者,当单纯的改值的时候这个参数为null,当被动监听变化时才不为null if (initiator != null) { //考虑通知方法,真正的通知逻辑执行处,这里当观察者装饰器不为空时直接通知给当前观察者 considerNotify(initiator); //并将当前观察设置为null initiator = null; } else { //当观察者为null时,也就是不是通知具体某个观察者,而是通知改数据的所有观察者 for (Iterator
, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { //遍历已有的观察者,挨个考虑通知观察者 considerNotify(iterator.next().getValue()); //如果有一次通知导致分发无效了,则直接终止循环, //这里决定如果有一个地方通知导致无效后会影响后继的观察者收到正确的通知 if (mDispatchInvalidated) { break; } } } //当分发无效标识为true时会一直循环,从源码看只有重复调用此方法时才会将该值改为true, } while (mDispatchInvalidated); //结束分发,标识值改为flase mDispatchingValue = false; } //考虑通知,也就是不一定通知,接收一个观察者装饰器 private void considerNotify(ObserverWrapper observer) { //如果观察者属于当前非活跃态则不发给它了 if (!observer.mActive) { return; } //如果观察者不应该是活跃态了,这里的具体实现其实是 isAtLeast(STARTED) if (!observer.shouldBeActive()) { //调用观察者活跃态变化方法,这个方法本质实现的是当前LiveData的观察者变化逻辑 observer.activeStateChanged(false); //不会分发 return; } //到了这里,说明观察者当前处于活跃态,且应该处于活跃态 //如果当前观察者的最新版本大于等于LiveData数据的当前版本,也就是数据的当前版本低于观察者记录的最新数据版本,也就是当前数据版本比较旧,那则没必要通知 if (observer.mLastVersion >= mVersion) { return; } //数据有新版本了,更新观察者的版本 observer.mLastVersion = mVersion; //调用观察者的onChanged方法,到这里就通知完了 observer.mObserver.onChanged((T) mData); } observe的逻辑
//标记是一个主线程中使用的方法 @MainThread public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) { //主线程断言 assertMainThread("observe"); //如果当前声明周期的拥有者的状态为销毁,则终止 if (owner.getLifecycle().getCurrentState() == DESTROYED) { return; } //将观察者进行装饰,这个装饰者其实大有文章 LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); //下面这个判断主要是防止不同的生命周期拥有者不能添加相同的观察者, //这里决定了如果出现复杂的观察者需要封装时,切莫做成单利或者多个Activity,Fragment公用一个观察者 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; } //都合理则将观察者的装饰器作为生命周期响应的观察者, //结合LifeCycle的粘性事件特性,当生命周期状态变化时也会响应变化, //如果这时候数据版本发生变化的话,就会最终走到onChanged方法,造成数据倒灌 owner.getLifecycle().addObserver(wrapper); }
观察者装饰类,一些核心思想的逻辑所在处
//这是观察者装饰类的抽象类,主要实现了针对活跃观察者的逻辑 private abstract class ObserverWrapper { //被装饰的观察者 final Observer super T> mObserver; //是否为活跃态 boolean mActive; //最新版本,这里可以看到初始时与mVersion的初始值是一样的 int mLastVersion = START_VERSION; //构造方法 ObserverWrapper(Observer super T> observer) { mObserver = observer; } //是否应当是活跃态,这里是个抽象,因为有两种实现,一个是正常的,根据atLast方法实现, //另一个是Always,用在observeForever的逻辑里,具体实现是始终返回true abstract boolean shouldBeActive(); //是否关联到owner,在判断同观察者绑定到不同生命周期拥有者的时候使用 boolean isAttachedTo(LifecycleOwner owner) { return false; } //解除观察者的关联 void detachObserver() { } //很关键的方法,当活跃态发生变化时调用 void activeStateChanged(boolean newActive) { //如果新的活跃态与当前的活跃态一直,则不会继续执行 if (newActive == mActive) { return; } //修改当前活跃态为目标活跃态 mActive = newActive; //当前LiveData的活跃态观察者数量为0时视为不活跃态 boolean wasInactive = LiveData.this.mActiveCount == 0; //LiveData活跃态观察者数量基于当前活跃态进行+ - 1 LiveData.this.mActiveCount += mActive ? 1 : -1; //LiveData没有活跃态观察者,本次变为活跃态时,也就是第一次有活跃态时,回调onActive, //这里时LiveData可以根据有无活跃态执行特定逻辑的功能的出处 if (wasInactive && mActive) { onActive(); } //LiveData的活跃态为0了,且本次更新变为了不活跃,说明一个活的都没有了,执行onInactive() if (LiveData.this.mActiveCount == 0 && !mActive) { onInactive(); } //如果最新是活跃态,则通知当前观察者执行数据变化发送 //可以看到,当观察者活跃态变为活跃态时,会去发一下最新的数据变化通知 //这里就是数据倒灌的直接上层原因,如果一个观察者变为活跃态之前,有了数据变化,主要是导致了mVersion++的话,观察者变为活跃态时自然就会收到最新的数据 //这里之所以说是直接原因,是因为要走到这里必须要自动监听到状态变化才行 //这里也是LiveData能自动接收最新数据变化功能的原因 if (mActive) { dispatchingValue(this); } } } //observe()方法时需要创建的观察者装饰器的实现, //这里要注意,除了是一个ObserverWrapper的实现外,同时还实现了LifecycleEventObserver接口 class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { //生命周期拥有者 @NonNull final LifecycleOwner mOwner; LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer super T> observer) { super(observer); mOwner = owner; } //这个是应当活跃的实现,可以看到本质是isAtLeast,也就是STARTED和RESUMED状态视为活跃态 @Override boolean shouldBeActive() { return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } //这里是LifecycleEventObserver的实现,这个方法当生命周期状态变化时作为生命周期的观察者会执行此方法, //还有addObserver时会执行此方法,这个方法的执行其实就是数据倒灌的根级触发点 @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (mOwner.getLifecycle().getCurrentState() == DESTROYED) { //如果是销毁态,直接移除当前观察者, //这里就是为啥LiveData添加的观察不会导致内存泄漏的原因了, //因为到这个状态前,是在onDestroy前,也就是onDestroy前就会自动移除无效的观察者了 removeObserver(mObserver); return; } //灵魂入口,调用父类的活跃态变化方法 activeStateChanged(shouldBeActive()); } //是否关联到声明周期拥有者,其实就是新建的和老的是不是一个生命周期拥有者 @Override boolean isAttachedTo(LifecycleOwner owner) { return mOwner == owner; } // 解除生命周期观察者实现 @Override void detachObserver() { mOwner.getLifecycle().removeObserver(this); } }
总结点东西
粘性事件的原因
LifecycleRegistry-->addObserver-->statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));-->mLifecycleObserver.onStateChanged(owner, event);
数据倒灌的原因
LiveData--observe-->owner.getLifecycle().addObserver(wrapper)-->LifecycleBoundObserver-->onStateChanged(owner, event)-->activeStateChanged(shouldBeActive())-->dispatchingValue(@Nullable ObserverWrapper initiator)-->considerNotify(ObserverWrapper observer)-->observer.mObserver.onChanged((T) mData);
数据倒灌的解决方案
- 利用打标记的方式,第一次执行observe时不触发onChanged方法,这也是UnPeekLiveData的思路
- 利用新建如果不传值的方式结合中介LiveData思路,将行为给一个空的LiveData,因为空的时数据Version是一致的不会触发onChanged
数据倒灌方案的应用场景
防止数据倒灌,只是防止首次observe的场景,这有时候会与LiveData观察即可拿到最新数据的设计不符,需要基于实际情况使用