问题描述
先看代码
viewModel.getLoading().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable final Boolean b) {
//do something
}
});
这是一段很简单的livedata监听数据变化的回调,当loading数据产生变化时就会回调onChanged方法。然而,现实问题是只在第一次触发时回调,以后无论数据怎么改变都不执行回调。
问题原因
先看触发setValue的地方,是在一个网络回调中
RequestCallback<TaskInfoEntity>() {
@Override
public void onSuccess(final Entity entity) {
....
loading.setValue(true)
....
}
@Override
public void onFail(final Throwable e, final String errMsg) {
}
}
当onChanged回调中发生异常时,就会产生上述问题,由于异常被网络回调catch住在onFail中回调,所以异常没有导致程序退出,问题也就变得很诡异。
那么为什么onChanged回调中发生异常时就会导致onChanged在后面的数据改变时不会回调呢?
这里看下LiveData 的dispatchingValue代码
private void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
....
//调用onChanged
....
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
dispatchingValue在调用onChanged前回先设置mDispatchingValue为true,并且判断如果mDispatchingValue为true后续代码就不会执行,当onChanged调用结束时再将mDispatchingValue设置为false,保证下次onChanged能够正常执行。
当onChanged代码发生异常并且被onChange以外的代码捕获了异常,mDispatchingValue = false就不会执行,mDispatchingValue永远为true,下次onChange就永远不会执行。
总结
类似问题发生前提
- onChanged 回调中发生异常
- setValue被包含在外部try catch中
- onChanged函数中没有做异常捕获
在网络回调中设置setValue很常见,如果在异常中又没有将异常展示,问题将很难发现,这里记录下