Android Jetpack系列之LiveData的基本逻辑解
2022-05-02 本文已影响0人
Android程序员老鸦
LiveData的出现是为了解决什么问题呢?
在我看来是为了解决开发者在项目开发中自有的一些数据或状态信息变更在有效的生命周期的传递。
在LiveData之前线程之间的数据传递我们用的是Handler或经过封装后的Rxjava和EventBus这些,诚然以上的组件都能达到目的,但是LiveData是正儿八经的围绕着数据传递和配合Jetpack标准框架来设计的,它小巧灵活,目标精准,就像一把简单精致的螺丝刀就是用来拧螺丝的,而不是像一个集螺丝刀剪刀扳手为一体的工具集,看似功能齐全,但是各个都不好用。
LiveData.png
Rxjava.png
具体好处我们在分析的时候慢慢品,抽丝剥茧地观摩它的设计和良苦用心。
先看一下它的基本使用,先在viewModel里定义:
class MainViewModel :ViewModel(){
private val testLiveData = MutableLiveData<Int>() //注意这个是MutableLiveData
fun getTestLiveData():LiveData<Int>{ //但是get方法暴露出去的是LiveData
return testLiveData
}
fun doNetWork(){
// 模拟网络请求后。。。
testLiveData.postValue(666)
}
}
然后在Activity里使用:
class MainActivity : AppCompatActivity() {
private lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel = ViewModelProvider(this)[MainViewModel::class.java]
// 拿到liveData后添加订阅关系
mainViewModel.getTestLiveData().observe(this){
Log.d("TAG","$it")
}
// 模拟网络请求拿到数据
mainViewModel.doNetWork()
}
}
打印信息:
2022-04-26 09:34:14.062 6131-6131/com.example.jetpackkk D/TAG: 666
又是典型的订阅通知观察者的套路,先来看看LiveData这个类observe方法:
public abstract class LiveData<T> {
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
...略
// liveData在添加订阅者的时候要求在主线程,简直就是为了让你拿到数据后更新UI量身定制
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// 如果页面已经处于destory,则不让其添加订阅
return;
}
// 这里注意了,传进来的LifecycleOwner和订阅者被封装在了LifecycleBoundObserver里,就
//是在这里把订阅者的响应跟组件的生命周期关联起来了
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 这里判了个重复添加,我记得kotlin的lambda表达式和java混用的时候会报这个异常
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
// 把包装的wrapper添加到Lifecycle的订阅者中,说明实际上订阅了两个被观察者,wrapper出现在了两个map里
owner.getLifecycle().addObserver(wrapper);
}
...略
}
LiveData观察者的类和包裹类,单独拿出来:
//
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}
// GenericLifecycleObserver 往上找会发现是继承于LifecycleObserver
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@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);
}
// 这个方法是LifecycleEventObserver的方法,liveData的通知是基于生命周期变化的时候下发的,因为
//它要保证订阅者在正常的生命周期里接受数据。
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
// 时刻在检查页面的生命周期是否合法,所以使用者不用担心内存泄漏这些问题
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
// 父类ObserverWrapper 的方法,看名字的意思是活跃状态改变了的意思
activeStateChanged(shouldBeActive());
}
@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;
// 获取当前活跃的监听者
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive(); // 钩子方法,活跃的监听者从无到有的时候的回调
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive(); //监听者从有到无的回调,可以实现一些定时的监听技巧
}
if (mActive) {
// 传值给此订阅者,单点触发订阅事件
dispatchingValue(this);
}
}
}
dispatchingValue(@Nullable ObserverWrapper initiator)方法是liveData里的方法,这个方法在liveData设置值的时候也会被调用,只不过它传的ObserverWrapper是null,而在ObserverWrapper 被调用则是因为生命周期变更的时候被触发的。
从这里可以看出LiveData设计的巧妙之处,它的订阅者在订阅liveData的时候其实订阅了两个被观察者,一个是liveData本身,另一个是所在的组件的Lifecycle,他们的变更都会触发订阅者的回调:
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);// 通知所有的订阅者,注意这里传的是null
}
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
// mDispatchingValue,表示正在分发,mDispatchInvalidated 表示无效分发
// 这里用这两个boolean值来处理分发的时候的并发逻辑
// 如果正在分发,则把这次标记为无效分发
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
// 标记成正在分发
mDispatchingValue = true;
do {
// 无效分发标志恢复false
mDispatchInvalidated = false;
// 不为null可以认为是值变更的时候,订阅者所在的组件生命周期处于不活跃状态(比如Stoped),忽然又变为活跃状态了
//由lifecycle触发的通知单个订阅者的情况
if (initiator != null) {
// 考虑通知这个订阅者
considerNotify(initiator);
// 把initiator 置为false,这样当mDispatchInvalidated为true的时候就会进入下面的else
initiator = null;
} else {
// 走这里则是通知所有的订阅者,可以认为是LiveData设置新的值后触发的订阅事件分发
// 遍历分发订阅事件,这里的mObservers是个SafeIterableMap,这种map支持迭代的时候对map进行添加删除元素操作
for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
// 分发值给订阅者
considerNotify(iterator.next().getValue());
// 此时如果mDispatchInvalidated为true,说明短时间内livedata频繁分发了2个以上的
//值,那么就会跳出上面的for循环,让旧值不再分发下去,接着外面的while循环会再
//走一此,然后继续走for循环,这样就能保证订阅者拿到最新的值
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
mDispatching和mDispatchInvalidated控制的那个双重循环的作用,我们跑个demo就知道效果:
class MainActivity : AppCompatActivity() {
// 初始化一个liveData
private val testLiveData = MutableLiveData(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 添加订阅
testLiveData.observe(this){
Log.d("TAG","收到变更事件 testValue = $it")
}
// 连续变更liveData的值
Log.d("TAG","开始改变liveData的值")
testLiveData.value = 1
testLiveData.value = 2
testLiveData.value = 3
Log.d("TAG","结束liveData的值变更")
}
}
打印结果:
2022-05-01 23:38:49.960 20215-20215/com.xgimi.jetpackk D/TAG: 开始改变liveData的值
2022-05-01 23:38:49.961 20215-20215/com.xgimi.jetpackk D/TAG: 结束liveData的值变更
2022-05-01 23:38:49.971 20215-20215/com.xgimi.jetpackk D/TAG: 收到变更事件 testValue = 3
最终订阅者只收到变更为3的事件。
继续看considerNotify(ObserverWrapper observer)方法,这个方法最终触发了订阅者的响应方法onChanged(),当然也设置了一些条件判断:
private void considerNotify(ObserverWrapper observer) {
// 如果订阅者的状态不是活跃的,则不传递值
if (!observer.mActive) {
return;
}
// 再次判断所在的那个组件是否活跃,为什么搞双重判断,按照官方的解释,即使订阅者的
//状态是活跃的,但是生命组件如果不是活跃的,那么也不会下发通知
// 那个包裹类重写了shouldBeActivie方法,规定至少是STARTED状态才是活跃的
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
// mLastVersion 初始值为-1 ,mVersion是liveData的全局变量,初始值也为-1,但是每次
//setValue会+1,这里的目的是为了防止重复分发(假设A页面有个订阅者,它由onStop回到
//了onResume,期间liveData并没有改变值,但是因为生命周期的变更触发了到了这里,这时候
//这里的判断就能拦住liveData不触发第二次订阅事件)
// 然而这里会引发一个粘性事件的问题,也即订阅动作即使是在setValue之后还是会收到上一次的事件,本质上还是每次
//添加订阅者的时候都会触发lifecycle组件之前的生命周期事件,然后走到这个方法里,再然后因为这
//个版本判断逻辑拦截不住最终触发了回调
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 最终执行了订阅者的通知方法回调
observer.mObserver.onChanged((T) mData);
}