1. 基本使用
LiveData 同 Lifecycle 一样,基于观察者模式构建及使用,官方推荐和 ViewModel 配合使用,简单范例如下
public class LiveDataViewModel extends ViewModel {
private MutableLiveData<String> stringMutableLiveData;
public MutableLiveData<String> getStringMutableLiveData() {
if (stringMutableLiveData == null) {
stringMutableLiveData = new MutableLiveData<>();
}
return stringMutableLiveData;
}
}
public class LiveDataActivity extends AppCompatActivity {
private LiveDataViewModel model;
// LiveData测试展示View
private TextView tvContent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_live_data);
tvContent = findViewById(R.id.tv_content);
model = ViewModelProviders.of(this).get(LiveDataViewModel.class);
// 订阅
model.getStringMutableLiveData().observe(this, observer);
}
// LiveData 测试事件观察者
private Observer<String> observer = new Observer<String>() {
@Override
public void onChanged(String s) {
if (tvContent != null) {
tvContent.setText(s);
}
}
};
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_live_data:
model.getStringMutableLiveData().postValue(String.format("测试LiveData : %s", System.currentTimeMillis()));
break;
}
}
}
上述代码做了这样一件事情:
- 创建一个包含了一个 TextView 和 一个 Button 的 activity
- 创建 LiveData ,并将 LiveData 与当前 activity 绑定起来
- 点击按钮: 获取当前时间戳,通过 LiveData 发送给观察者
- 观察者收到回调,将这个时间戳更新到 TextView 上
运行效果如下:
主要步骤拆解:
- ViewModel 内定义 LiveData 实例
LiveData为抽象类,具体实现为MutableLiveData<T>
- 通过
LiveData.observer(FragmentActivity activity, Observer oserver)将LiveData 和当前 activity 绑定起来:- 这样做是为了感知 activity 的生命周期,当 activity 消亡后,不再接收数据更新
- ViewModel 可以通过
ViewModelProviders.of(FragmentActivity activity).get(Class<T> modelClass)初始化
2. 原理分析
2.1 更新数据
更新数据主要分为两种:
- 子线程:
LiveData.postValue(T value) - 主线程:
LiveData.setValue(T value)
其中子线程通过 postValue() 的方式更新数据时,只是通过 Handler 进行了一层包装,切换到了主线程,最终调用的还是通过调用 setValue() 完成的更新。
2.1.1 流程分析
第一步: 看下 setValue() 做了什么
private int mVersion;
private volatile Object mData;
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
这里主要出现有两个关键点:
- 两个变量
mVersionLiveData 用于记录数据更新的版本号, 每次通过setValue()更新数据就会加1mDataLiveData 内成员,保存了最新一次setValue()时更新数据的副本
- 调用
dispatchingValue(null)为具体的数据更新逻辑,(注意这里传入的实参为null)
第二步:
看下dispatchingValue() 实现
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
// 因为传入的是 null 所以不会执行这个 if
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;
}
主要有几个点:
- 两个变量
mDispatchingValue和mDispatchInvalidated- 两个变量都是为了递归调用的case, 如:当上述 observer 的
onChanged()被回调时,内部又调用了LiveData.setValue()来更新数据,这时正常来说因为方法的递归调用会导致无限循环,而这两个变量就是为了防止这种情况而设置,具体思想和 Lifecycle 中对递归调用的防止思想基本类似,详细可以参考这篇文章:Android架构组件(2)LifecycleRegistry 源码分析
- 两个变量都是为了递归调用的case, 如:当上述 observer 的
- 因为传入的是
null, 所以执行 else 分支的逻辑 - 遍历
mObservers通知所有观察者:数据更新啦~,新的数据是XX~~~~- 这里的
mObservers为一个 Map 结构,保存了LiveData.observer(FragmentActivity activity, Observer oserver)时所有的观察者 - 具体更新方法封装在了
considerNotify()内
- 这里的
第三步:
看下 considerNotify() 实现
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()将最新数据同步给了观察者 - 将 observer.mLastVersion 更新为了 mVersion
- 通过最后一行代码,调用
- 前边还有三个 if 判断用于异常拦截
- 前两个表示当前 activity 是否处于可见状态 (onResume被调用,但是 onPause 未被调用的交互态),也是为什么当前 actvity 不可见时, LiveData的更新不会被感知到
- 第三个 if 稍后分析
这里出现了两个 版本号, observer.mLastVersion 和 LiveData.mVersion, 要弄清楚,需要结合订阅代码,回到订阅处:
使用 LiveData 时的代码:
model.getStringMutableLiveData().observe(this, observer);
public abstract class LiveData<T> {
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 将观察者进行了一次包装
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");
}
// 如果已经订阅过了,无需再订阅 lifecycle, 用于递归调用时case的处理
if (existing != null) {
return;
}
// 用包装后的 wrapper ,订阅了 activity 生命周期变化的事件
owner.getLifecycle().addObserver(wrapper);
}
}
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
// ...省略代码
}
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
}
几个点:
- 传入的
observer就是 Activity 中我们用于观察数据变化的观察者,这里又被包装成了LifecycleBoundObserverLifecycleBoundObserver继承了ObserverWrapper, 所以也继承了mLastVersion,所以上边的observer.mLastVersion就是LifecycleBoundObserver.mLastVersion- 因为可以有很多观察者,所以这里同 Lifecycle 一样,使用 Map 保存,变量就是这里的
mObservers
- 最后使用这个包装
LifecycleBoundObserver对象订阅了当前 Activity 上的 Lifecycle 变化事件- 这也同时解释了为什么要包装成
LifecycleBoundObserver, 因为 Lifecycle 的订阅者可能会通过 APT生成,通过反射等诸多不同方式调用,所以包装一层,统一面对LifecycleEventObserver接口调用,更加方便管理,具体参见 Lifecycle 的实现原理 - 也是 LiveData 能够感知 Activity 生命周期变化的又一次证明
- 这也同时解释了为什么要包装成
回到最初的问题上,结合这里的逻辑,梳理可得:
- 可能有多个观察者观察 LiveData 上的数据变化,所以是 LiveData 这个被观察者和诸多观察者的一对多的关系
- 两个版本号:
- LiveData 上的
mVersion用于记录当前 LiveData 上数据更新的版本/次数- 诸多 Observer 上各自维护一个自己
mLastVersion用于表示当前观察者收到的数据的版本- 每次 LiveData 将数据通知到一个观察者上时,将其
mLastVersion更新为自己的mVersion的值
LiveData 和其诸多观察者的关系体现为:
结合上述代码,再将 LiveData 和 Activity 的关系引入后,将是:
3. 粘性事件
LiveData 的正常使用步骤为:
- 观察者订阅 LiveData 数据更新事件
- LiveData 通知订阅者数据更新了
- 观察者收到了更新的数据
但是在使用过程中发现, 即使 步骤1 和 步骤2 顺序反了,观察者最终还是会收到数据更新的结果, 这种情况称为 LiveData 的粘性事件
PS: 但是有个特殊情况需要关注 如果多次调用
setValue()方法,进行数据更新,之后再使用观察者订阅LiveData的数据变化,在产生订阅关系后,只能收到最后一次setValue()的值。
3.1 常规粘性事件
很简单,先回到 dispatchingValue 方法上
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// 还没订阅,所以 mObservers 中取不到相应观察者
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
因为还没有通过 observe 产生订阅关系,所以在遍历过程中拿不到相应的观察者,所以就会一直在这里空转,但是一旦订阅后,这里的 mObservers 可以拿到相应 observer了,就可以直接通过 considerNotify() 通知观察者更新数据了
然后再回到 considerNotify()
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.((T) mData);
}
很显然
- 首先前两个 if 肯定没有命中,没啥可说的
- 又因为当前
observer为初次订阅,所以mLastVersion一定为初始值,查源码得知为-1 - 又因为
mVersion初始值为0, 且setValue()每次调用都会执行mVersion++, 所以这里observer.mLastVersion >= mVersion也一定不成立 - 这样,事件最终就通过最后的
onChanged更新到观察者那里了,也就产生了 粘性事件
3.2 为什么多次只能收到一次更新
那么多次 setValue() 再订阅后,为什么只能收到最后一次更新呢?
也很简单,LiveData 内用的 mData 记录数据,每次都会被覆盖罢了
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
// 每次都会覆盖上一次的值
mData = value;
dispatchingValue(null);
}