1. LiveData简介与使用
1.1 简介
LiveData
是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 activity、fragment 或 service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
1.2 简单使用
- 通常LiveData都是作为ViewModel层存在于ViewModel中
kotlin
class NameViewModel : ViewModel() {
private val _currentName: MutableLiveData<String> = MutableLiveData()
// 只暴露不可变的LiveData的目的是防止外界变更,使得ViewModel才是唯一可信来源
val currentName: LiveData<String> = _currentName
fun fetchUserName() {
viewModelScope.launch {
delay(2000)
_currentName.value = "BTPJ"
}
}
}
kotlin
class NameActivity : AppCompatActivity() {
/** 使用activity-ktx依赖中的代理直接赋值 */
private val model: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_name)
// LiveData监听
model.currentName.observe(this) {
findViewById<TextView>(R.id.tv_name).text = it
}
}
}
- 更多关于LiveData的
扩展
、转换
以及合并
可直接查看官方教程,这里主要分析LiveData的源码
2. 源码分析
2.1 添加观察者
- LiveData主要是用到了观察者模式,其中添加观察者是通过
observe()
java
// LiveData.java
/**
* 添加观察者. 事件在主线程分发. 如果LiveData已经有数据,将直接分发给observer。
* 观察者只在LifecycleOwner活跃时才接收事件,如果变为DESTROYED状态,observer自动移除。
* 当数据在非活跃时更新,observer不会接收到。变为活跃时 将自动接收前面最新的数据。
*
* @param owner 观察绑定的LifecycleOwner
* @param Observer 要添加的观察者
*/
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 限制主线程调用
assertMainThread("observe");
// 当绑定的LifecycleOwner为DESTROYED状态时直接忽略
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
return;
}
// 将owner和observer一起包装成LifecycleBoundObserver对象
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 将observer作为key,wrapper作为value存入Map中,putIfAbsent表示当前是否存在该
// observer,存在就直接返回已存储的ObserverWrapper值,不存在就直接添加
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
// 同一个观察者与不同的LifecycleOwner绑定直接报错
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
// 已存在就不再重复绑定
if (existing != null) {
return;
}
// 不存在就让wrapper观察owner的生命周期,即绑定生命周期
owner.getLifecycle().addObserver(wrapper);
}
- 主要做了两件事
*- 将观察者缓存进Map
-
- 将观察者的观察周期与LifecycleOwner的生命周期绑定
2.2 事件分发回调
2.2.1 setValue/postValue
- 先从LiveData设置数据开始着手,postValue是任意线程(主要针对子线程)调用,而setValue则只能是主线程调用
java
/** 从其他线程发送数据到主线程 */
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
// 数据缓存至mPendingData
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) {
// 取出postValue()缓存的value
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 最终还是调用了setValue(),只不过帮你切到了主线程
setValue((T) newValue);
}
};
- 由此可见
postValue()本质上还是调用了setValue(),只不过帮你从子线程切到了主线程
,简单看下是怎么切的
java
// DefaultTaskExecutor.java
@Override
public void postToMainThread(Runnable runnable) {
if (mMainHandler == null) {
synchronized (mLock) {
if (mMainHandler == null) {
mMainHandler = createAsync(Looper.getMainLooper());
}
}
}
mMainHandler.post(runnable);
}
- 没有其他办法,果然还是通过
Handler
- 接着看下setValue()
java
@MainThread
protected void setValue(T value) {
// 限制只能在主线程调用
assertMainThread("setValue");
mVersion++;
mData = value;
// 关键事件分发
dispatchingValue(null);
}
- 接着看分发事件的方法
dispatchingValue(null)
,注意传递的参数为null
java
/** 分发事件 */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
// setValue传值为null走的这里
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
- 可以看到最终会遍历所有的观察者,并逐个调用
considerNotify(observer)
java
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
// 若当前observer对应owner非活跃,就会调用activeStateChanged方法,
// 并传入false,其内部会再次判断
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
// 这里就是关键的onChanged(mData)回调,会在活跃状态执行
observer.mObserver.onChanged((T) mData);
}
/** 判断是否是活跃状态(RESUMED状态) */
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
- 整个过程就是一个典型的观察者模式,只是其中融入了对LifecycleOwner是否活跃的判断,只有在活跃时才回调。
2.2.3 粘性事件
- 粘性事件 :
即发送消息事件早于注册事件,依然能够接收到消息通知
- LiveData默认就支持粘性事件(而且没有关闭开关),简单分析下支持的原理,主要切入点还是所谓后注册的注册事件observe()
java
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
...
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
...
owner.getLifecycle().addObserver(wrapper);
}
- 核心在于
owner.getLifecycle().addObserver(wrapper)
,addObserver()是Lifecycle库用来添加一个LifecycleObserver,当LifecycleOwner改变状态时,它会被通知,通知回调是onStateChanged(...)
,看下这个wrapper(即LifecycleBoundObserver对象)的onStateChanged()方法
java
// LiveData.java
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
// 观察者是DESTROYED状态时移除观察
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
// 关键代码
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
...
}
- 与上面一样最终还是会执行
activeStateChanged()
,传参为shouldBeActive()即是否处于活跃状态
java
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
// 当判断为活跃状态时即会执行下面分发事件代码
dispatchingValue(this);
}
}
- 最终会执行dispatchingValue(this),流程跟上面一样,只是传参不再是null了,简单再看下里面的逻辑
java
void dispatchingValue(@Nullable ObserverWrapper initiator) {
...
do {
mDispatchInvalidated = false;
if (initiator != null) {
// 有值会执行这里
considerNotify(initiator);
initiator = null;
} else {
...
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
- 可见最终还是会执行considerNotify(initiator),只不过不再是循环执行所有观察者,而是只针对那一个观察者
3、LiveData存在的问题
3.1 数据倒灌
3.1.1 问题描述
- 所谓数据倒灌,其实就是新添加的监听依然会收到老数据的发送,其实很好理解,就是
粘性特性带来的弊端
,粘性特性有时候很有用,但有时候又会带来灾难,但LiveData官方并没有提供像EventBus那样取消粘性的Api,所以这个灾难就产生了一个新词即数据倒灌。 - 举一个简单又常见的例子:ViewModel数据请求时出错了,我们Activity收到错误进行Toast提示,这时当Activity配置改变(如旋转屏幕),这个错误会又提示一遍
kotlin
class MainViewModel : BaseViewModel() {
private val _errorLiveData = MutableLiveData<String>()
val errorLiveData: LiveData<String> = _errorLiveData
fun loadData() {
viewModelScope.launch {
LogUtil.d("LTP","开始请求")
delay(1000)
_errorLiveData.value = "模拟请求出错"
}
}
}
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mViewModel.apply {
errorLiveData.observe(this@MainActivity) {
LogUtil.d("LTP", it)
ToastUtil.showShort(this@MainActivity, it)
}
// 避免说是受到多次请求的影响,加个判断只请求一次
if (errorLiveData.value == null) {
loadData()
}
}
}
}
运行结果:
开始请求
模拟请求出错
-----屏幕翻转后----
模拟请求出错
- 原因很简单,屏幕翻转时,会重新执行Activity的onCreate(),observe()会再次执行,由于粘性事件的原因,那个错误值依然会被接收到
3.1.2 解决方式
- 所谓处理数据倒灌最典型的思路一种是直接让LiveData取消粘性特性,一种是在自定义LiveData并在onChange()回调中做判定
- 使用第三方库UnPeek-LiveData
- 使用事件包装器
- SingleLiveEvent
- 反射干涉Version
3.2 postValue导致的数据丢失
3.2.1 问题描述和产生原因
- 问题描述: 当使用postValue()发送数据时,生产速度(postValue)超过消费速度(onChange)时,会存在中间数据丢失的问题
- 产生原因: 回顾下postValue
java
/** 从其他线程发送数据到主线程 */
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
// 数据缓存至mPendingData
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) {
// 取出postValue()缓存的value
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 最终还是调用了setValue(),只不过帮你切到了主线程
setValue((T) newValue);
}
};
- 当发送的旧消息还未及时消费,新消息中的value就会赋值到缓存的mPendingData,再进行消费时取出的newValue就是新的value,从而导致旧的value丢失
3.2.2 解决方式
- 可以使用Kotlin的Flow(支持缓存背压),或者Rxjava的flowable
3.3 数据抖动
- 原因:由于LiveData并未对发送的旧值与新值做对比,所以当同一个数据被多次发送时都会被回调执行
- 解决方案 :自定义LiveData在onChange中判断处理或者将LiveData换成
stateFlow