什么是LiveData
LiveData是一个可以感知(Activity/Fragment)生命周期的数据容器,它具备如下几个特性:
- 它会在宿主生命周期进入
DESTORY
状态时自动移除掉观察者。 - 当宿主的生命周期处于活跃状态
STARTED
、RESUMED
状态才会触发数据的分发。 - 它是线程安全的,它内部使用的
Synchronized
处理线程安全问题。
LiveData使用
LiveData使用时,我们可以在主线程中通过setValue更新数据,在子线程中可以通过postValue更新数据。代码如下:
class MainViewModel : ViewModel() {
private val _userLiveData = MutableLiveData<String>()
val userLiveData: LiveData<String>
get() = _userLiveData
fun setUser(name: String) {
_userLiveData.value = name
}
fun postUser(name: String) {
_userLiveData.postValue(name)
}
}
class MainActivity : AppCompatActivity() {
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel.userLiveData.observe(this) {
Log.d("MainActivity", "userName:$it")
}
//主线程中更新LiveData数据
viewModel.setUser("小虎")
//子线程中更新LiveData数据
Thread {
viewModel.postUser("小米")
}.start()
}
}
LiveData源码分析
生命周期
我们说LiveData是可以感知生命周期的数据容器,我们来看一下,为什么可以感知生命周期。我们来分析observe
方法,源码如下。
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
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");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
我们看到我们注册的Observer
被放进了LifecycleBoundObserver
中,LifecycleBoundObserver被放进了mObservers
集合中,如果集合中存在不会重复添加,最后LifecycleBoundObserver被注册给lifecycle。
LifecycleBoundObserver
我们再看一下LifecycleBoundObserver
中做了什么。源码如下:
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) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
}
我们看到LifecycleBoundObserver
继承了ObserverWrapper
,我们自己写的Observer会被包装到ObserverWrapper中。同时LifecycleBoundObserver实现了LifecycleEventObserver
,这样该类就具备了观察Activity/Fragment生命周期的能力,在observer
方法中调用了mOwner.getLifecycle().addObserver()
注册该观察者,这样当我们的宿主生命周期发生变化时,就会调用onStateChanged
方法。
onStateChanged
上面说到宿主的生命周期发生变化后,会调用onStateChanged
方法,我们看一下该方法中有做了什么处理。
public void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
该方法会先判断当前状态是否为DESTROYED
,如果是会把我们自己写的Observer移除。如果不是会调用activeStateChanged
,同时修改当前的状态。
activeStateChanged
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this);
}
}
在该方法中会修改我们自己注册的Observer的激活状态mActive
,如果是激活状态会调用dispatchingValue
分发数据。dispatchingValue方法这里暂时不详解,在后面setValue
方法时再做详细分析。
setValue
我们使用setValue时会直接修改LiveData的值同时修改LiveData版本号mVersion
,最后会去调用dispatchingValue
分发数据。这里的数据分发不一定会真实的触发数据的分发,因为数据的分发需要几个条件,宿主的生命周期处于活跃状态、数据版本号发生改变才会去触发。
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
dispatchingValue
上面我们说到宿主的生命周期发生变化和setValue方法都会调用该方法,我们来看一下该方法是如何分发数据的。源码如下:
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
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
判断当前LiveData是否正在分发数据,如果正在分发数据修改mDispatchInvalidated
为true,表示正在分发的数据为无效状态,会使正在分发的数据重新进入while循环,去使用最新的数据做分发。这种情况大部分在多线程或数据发送过快时出现。如果当前LiveData没有正在分发数据,会分两种情况分发数据1.有固定的监听者,只会把数据分发给该监听者。该情况可以查看生命周期状态变化时的分发数据。2没有固定的监听者,会遍历所有的监听者并分发数据。分发数据会调用considerNotify
方法。
considerNotify
我们看一下该方法是如何具体分发数据的,源码如下:
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
我们可以看到当宿主处于活跃状态,会去比较observer中的数据版本号mLastVersion
和当前LiveData的数据版本号mVersion
,如果LiveData当前的版本大,会调用observer的onChanged方法分发数据。这里数据分发的条件之一是比较的数据版本,而不是数据本身,也就是说我们的数据相同,版本号不同也是会重新分发数据的。
postValue
上面我们分析了如何在主线程中调用setValue更新数据,下面我们看看如何在子线程中调用postValue更新数据的。
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
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) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
这里会先判断是否有正在处理分发的任务,然后把数据给一个临时变量mPendingData
,如果有正在分发的任务该方法就结束了。没有正在分发的任务会调用ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)
在主线程中处理,这里使用的是Handler的post,这里就详细说了。在Runnable中还是调用的setValue方法去更新数据的。
LiveData常见问题
数据丢失
数据丢失分为多种情况。
一种是宿主在非活跃状态多次调用setValue和postValue会导致中的的数据无法接收,造成数据丢失。
另外的一种是postValue造成的数据丢失,postValue为什么会造成数据的丢失,源码如下:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
//代码2
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
private final Runnable mPostValueRunnable = new Runnable() {
@SuppressWarnings("unchecked")
@Override
public void run() {
Object newValue;
//代码1
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
setValue((T) newValue);
}
};
-
在主线程执行到代码1之前的位置,再次调用postValue时,因为mPendingData不为NOT_SET,会在更新mPendingData完值后退出该方法,造成数据丢失。
-
在多线程的情况下,当一个线程执行到代码2的位置,另外的一个线程执行postValue方法,因为mPendingData不为NOT_SET,会在更新mPendingData完值后退出该方法,造成数据丢失。
粘包和数据倒灌
粘性事件
什么是粘性事件,就是我们先修改LiveData值,再注册Observer仍然能收到数据。如下代码在activity生命周期变成活跃状态时就会发生粘性事件。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val liveData=MutableLiveData<String>()
liveData.value="冬至"
liveData.observe(this) {
Log.d("MainActivity", "收到数据:$it")
}
}
}
我们来分析为什么会发生粘性事件,有两个地方导致的。
一是因为在setValue时会修改LiveData的版本号mVersion++
,而新注册的Observer的mLastVersion=-1
,数据分发时对比的是LiveData的mVersion
与Observer的版本mLastVersion
,所以mVersion>mListVersion触发数据分发。
数据倒灌
Activity异常销毁然后重建,ViewModel会保存销毁之前的数据,然后在Activity重建完成后进行数据恢复,所以LiveData成员变量中的mVersion
会恢复到重建之前的值。但是Activity重建后会调用LiveData的observe()方法,方法内部会重新new一个LifecycleBoundObserver
实例,其内部的mLastVersion=-1
由于粘性事件的特性,Activity的生命周期由非活跃变成活跃时,LiveData会触发事件分发出现数据倒灌。
解决粘性事件或数据倒灌
- 重写LiveData的observer方法,通过反射拿到LiveData的版本号
mVersion
,修改observer的mLastVersion
为mVersion
。这样可以保证版本号的一致,把粘性事件变成非粘性事件,也可以解决数据倒灌的问题。 - 某位大神提供的SingleLiveEvent,可以解决数据倒灌问题,不能解决粘性问题。
UnPeekLiveData
,这个是KunMinX开源的一个解决此类问题的方法,其可以把粘性变成非粘性,同时可以防止数据倒灌。其思路是,为每个传入的observer对象携带一个布尔类型的值,作为其是否能进入observe方法的开关。每当有一个新的observer存进来的时候,开关默认关闭。每次setValue后,打开所有Observer的开关,允许所有observe执行。同时方法进去后,关闭当前执行的observer开关,即不能对其第二次执行了,除非你重新setValue。