前言
LiveData更新数据的方法。创建一个LiveData,调用observe方法,传入生命周期owner对象和自定义的观察者。
kotlin
val data:LiveData<String> = MutableLiveData("test")
data.observe(this,object :Observer<String>{
override fun onChanged(value: String) {
println("data changed $value")
}
})
LiveData源码中的重要属性和方法
java
public abstract class LiveData<T> {
// 改变数据后的标记
private int mVersion;
// 迭代更新数据的Iterable
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
// 检测是否时主线程
assertMainThread("observe");
// 检测owner生命周期
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
// 生命周期owner和观察者observer包装为wrapper
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
// 观察者observer作为key把wrapper存入集合mObservers
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;
}
// 包装后的wrapper给到LifecycleRegistry
owner.getLifecycle().addObserver(wrapper);
}
}
LiveData对象有mObservers集合,存储观察者(一个LiveData对象可以有多个观察者)。
java
@MainThread
protected void setValue(T value) {
// 检测是否在主线程
assertMainThread("setValue");
// 每次更新数版本属性++
mVersion++;
// 更新数据
mData = value;
// 分发数据
dispatchingValue(null);
}
从源码可以看到setValue需要在主线程调用。
java
protected void postValue(T value) {
boolean postTask;
// 锁保护线程安全,即支持主线程调用
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
// 切换到主线程run一个runnable
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);
}
};
postValue一般在子线程调用,mPostValueRunnable(主线程)里调用了setValue。所以数据的更新postValue最终还是通过setValue更新的。分发数据:
java
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
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
considerNotify对observer状态过滤,版本判断,通过后,对mObserverde的onChanged进行数据回调。
kotlin
@SuppressWarnings("unchecked")
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.
// 检测当前mOwner状态是否符合(state>=STARTED 符合状态的有:STARTED和RESUME)
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
从上面看出LiveData更新数据必须在STARTED和RESUME状态,否则不分发。 mVersion是int类型,比long和double节省内存。mVersion++没有判断最大值(Integer.MAX_VALUE)判断,当达到最大值不会报溢出(integer overflow)异常,会绕到最小值int(Integer.MIN_VALUE=-2147483648),虽然不报错,由于mLastVersion>mVersion,会触发一次return。所以下次改变数据才会真的更新。
LiveData感知生命周期
LiveData本身不具备感知生命周期。由lifecycle实现的。LifecycleOwner,一般LiveData在Activity进行观察,传入this
kotlin
val data:LiveData<String> = MutableLiveData("test")
data.observe(this,object :Observer<String>{
override fun onChanged(value: String) {
println("data changed $value")
}
})
ComponentActivity实现了LifeCycleOwner,所以observe传this这样LiveData就能感知生命周期了。所以如果想自定义可以自己实现LifeCycleOwner传入到observe中。 自定义owner:
kotlin
/**
* 实现LifecycleOwner,体哦那个生命周期能力
*/
class CustomLifecycleOwner : LifecycleOwner, LifecycleObserver {
/**
*
*/
private val mRegistry = LifecycleRegistry(this)
init {
// 添加生命周期观察者
mRegistry.addObserver(this)
// 参考Activity生命周期流程,初始化生命周期状态
mRegistry.currentState = Lifecycle.State.INITIALIZED
}
override val lifecycle: Lifecycle
get() = mRegistry
/**
* 维护生命周期状态
*/
fun funCreate() {
mRegistry.currentState = Lifecycle.State.CREATED
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
fun funOnCreateCallback() {
println("funOnCreateCallback")
}
fun funStared() {
// liveData在owner状态>=STARTED才会分发数据
mRegistry.currentState = Lifecycle.State.STARTED
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun funOnStartCallback() {
println("funOnStartCallback")
}
fun funOnDestroy() {
mRegistry.currentState = Lifecycle.State.DESTROYED
// 取消注册
mRegistry.removeObserver(this)
}
}
测试:
kotlin
val data = MutableLiveData("test")
val owner = CustomLifecycleOwner()
data.observe(owner, object : Observer<String> {
override fun onChanged(value: String) {
println("data changed $value")
}
})
owner.funCreate()
data.value = "111"
Handler().postDelayed({
owner.funStared()
data.value = "222"
}, 2000)
可以看到observer分别打印了111和222日志。onCreate状态下,set=111,由于生命周期关系没有分发,所以没有收到。后面延迟2秒,onStart后,收到了111和222数据。可以看到liveData具有粘性。
LiveData具有粘性原因
从上面可以看到funStarted触发mRegistry.currentState = Lifecycle.State.STARTED触发流程:
kotlin
Lifecycle.State.STARTED-->setCurrentState-->moveToState-->sync-->forwardPass-->observer.dispatchEvent-->mLifecycleObserver.onStateChanged(owner,event)-->activeStateChanged(shouldBeActive())-->dispatchingValue(this)-->considerNotify(initiator)-->observer.mObserver.onChanged((T)mData)
当调用Lifecycle.State.STARTED的时候就会默认触发一次onChanged去更新observer观察者,粘性触发(发送距离Lifecycle.State.STARTED最近的一次数据更新)
liveData丢失数据的情况
如果子线程或多线程频繁调用liveData.postValue,就会丢失数据。官方不建议这样去使用LiveData(数据的改变没有缓存起来,就只是用了一个当前成员mData保存最新改变的数据)。非要用liveData处理,那就单线程每次更新加延迟,或者频繁切换线程setValue去更新,虽然可以,代价就是浪费性能。如果业务一定需要,建议用Rxjava或Flow。
Fragment使用LiveData
LiveData类中LifecycleBoundObserver,LiveData绑定的LiecycleOwner生命周期变化为onDestroy时会移除对应的观察者。所以Activity作为LifecycleOwner时可以自动处理LiveData的解注册问题。LiveData类中LifecycleBoundObserver中源码:
kotlin
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
// onDestroy的时候移除了Observer
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
然而,Fragment加载方式有add和replace两种
- 使用add加载新的Fragment,原本旧的Fragment不会被移除栈,旧的Fragment就不会触发onDestroy,LiveData就不会解注册
- 使用replace加载Fragment,旧的Fragment进行出栈,旧的触发onDestroy,LiveData可以解除注册。
Fragment用LiveData,那只能用replace不能用add吗。我们可以使用ViewLifecycleOwner,其实就是FragmentViewLifecycleOwner。官方推荐用viewLifecycleOwner,在视图不显示的时候,会触发onDestroy帮助我们进行LiveData解注册。
一直监听LiveData,无论是否活跃(STARTED)
可以使用observeForever函数,是没有传lifecycleOwner的只有观察者一个参数。没有owner就没有生命周期了。默认一直活跃,需要自己解注册处理。
kotlin
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true);
}
每次传入observer都会包装成一个新的wrapper,再调用每个新的wrapper的activeStateChanged(true)。一个LiveData支持多个observeForever,即使传同一个观察者,它也会包装为新的wrapper。
LiveData转换数据类型
kotlin
// 获取用户信息
val data = MutableLiveData<UserInfo>()
val userLv:LiveData<Int> = Transformations.map(data){
it.userLv
}
val userLv:LiveData<Int> = Transformations.switchMap(data){
MutableLiveData(it.userLv)
}
例如:使用一个接口的回调结果展示用户信息;后面还有其他业务需要监听用户等级变化,展示其他业务。使用Transformations可以实现。 除了map还有switchMap,switchMap每次返回一个新的LiveData对象。普通类型转换,用map就可以了。使用switchMap场景:
- 假如ViewModel暴露一个itemsLiveData给用户观察。我们通过用户的意图(提供一个带有参数的getItemsLiveData方法),用户可选择数据库的旧数据或网络请求的新数据
kotlin
// 这是本地数据源
private val localDataSource = MutableLiveData<List<Item>>()
// 这是网络数据源
private val networkDataSource = MutableLiveData<List<Item>>()
// 暴露给用户的liveData
val itemsLiveData = MutableLiveData<List<Item>>()
// 用于获取最终数据流的方法
fun getItems(dataSourceType: DataSourceType): LiveData<List<Item>> {
// 使用switchMap根据dataSourceType的值切换数据源
Transformations.switchMap(dataSourceType) { type ->
when (type) {
DataSourceType.LOCAL -> {
// 如果选择本地数据源,返回本地数据的LiveData
localDataSource
}
DataSourceType.NETWORK -> {
// 如果选择网络数据源,返回网络数据的LiveData
networkDataSource
}
}
}
}
switchMap每次都会创建一个新的LiveData不适合频繁调用情况,浪费性能。可以使用MediatorLiveData
kotlin
// 这是本地数据源
private val localDataSource = MutableLiveData<List<Item>>()
// 这是网络数据源
private val networkDataSource = MutableLiveData<List<Item>>()
// 暴露给用户的liveData
val mediatorLiveData = MediatorLiveData<List<Item>>()
// 用于获取最终数据流的方法
fun getItems(dataSourceType: DataSourceType): LiveData<List<Item>> {
// 使用switchMap根据dataSourceType的值切换数据源
when (dataSourceType) {
DataSourceType.LOCAL -> {
// 如果选择本地数据源,返回本地数据的LiveData
localDataSource.value = loadLocalData()
mediatorLiveData.addSource(localDataSource) {
mediatorLiveData.value = localDataSource.value
}
return localDataSource
}
else ->{
// 如果选择网络数据源,返回网络数据的LiveData
networkDataSource.value = getNetWorkData()
mediatorLiveData.addSource(localDataSource) {
mediatorLiveData.value = localDataSource.value
}
return networkDataSource
}
}
}
如果不用上面的方案,直接定义一个LiveData,数据来了,无论网络还是本地、别人传的,都用setValue,这样也行,但是不符合常理(需要根据业务分析是不是多余)。