一、为什么使用LiveData
在Android开发之初,大部分代码都放在一个Activity中,导致Activity臃肿且难以单元测试。后来,诞生了MVC
、MVP
、MVVM
等开发架构,通过分层抽离Activity中的代码。这种分层架构模式可以将逻辑从View
中分离出来,但是无法感知Activity的生命周期,Activity
的生命周期必须通知这些组件。
LiveData
是一种可观察的数据存储器类,具有生命周期感知能力,并可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者,这可以很好的解决上述的相关问题。
如果观察者的生命周期处于STARTED
或RESUMED
状态,则LiveData
会认为观察者处于活跃状态。LiveData通知观察者时会检查其实时状态,仅会通知处于活跃状态的观察者。一个观察者处于 PAUSED
或 DESTROYED
状态,它将不会收到通知。一旦观察者重新恢复 RESUMED
状态,它将会重新收到 LiveData
的最新数据。
LiveData
可以注册和实现LifecycleOwner
接口的对象配对的观察者,当相应的Lifecycle
对象状态变为DESTROYED
,便可移除此观察者,这样就可以防止内存泄漏问题(当 Activity
和 Fragment
的生命周期被销毁时,系统会立即退订它们)
LiveData
的特点:
- 基于观察者模式,是一种持有可被观察数据的类。其需要一个观察者对象,一般是
Observer
类的具体实现。 - 可以感知生命周期,在生命周期活跃时更新组件。其中
STARTED
和RESUMED
就是活跃状态 - 可以在生命周期结束时立刻解除对数据的订阅,从而避免内存泄漏等问题
- 可以解决
Configuration Change
问题,配置更改而重新创建Activity或Fragment,其会立即接收最新的可用数据。
二、基本使用
依赖引入
dependencies {
def lifecycle_version = "xxx"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
// Saved state module for ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
// Annotation processor
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
}
LiveData
对象一般使用步骤:
- 创建
LiveData
实例,指定源数据类型,通常在ViewModel
中完成 - 创建
Observer
实例对象,实现onChanged()
方法,用于接收源数据变化并刷新UI,通常在界面控制器(Activity
或Fragment
)中创建 -
LiveData
实例使用observer()
方法添加观察者,并传入LifecycleOwner
-
LiveData
实例使用setValue()/postValue()
更新源数据(子线程要postValue
)
2.1 简单使用
简单演示LiveData
的使用
Activity中创建LiveData对象
LiveData
是一个抽象类,不能直接使用,通常用它的直接子类MutableLiveData
来实现
class MainActivity : AppCompatActivity() {
companion object {
private const val TAG = "MainActivity"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mutableLiveData = MutableLiveData<String>() //指定源数据类型为String
mutableLiveData.observe(this) {
Log.d(TAG,"onChanged:$it" )
Toast.makeText(this,it,Toast.LENGTH_LONG).show()
}
mutableLiveData.postValue("Hello LiveData") //更新源数据
}
}
observer
方法有两个参数:
- 参数一:
LifecycleOwner
,前述代码为MainActivity
本身 - 参数二:
Observer<T>
,前述代码新建了一个Observer<String>
,在onChange
方法中回调
大多数情况,LiveData
的observe
方法放在onCreate
中,如果放在onResume
中会出现多次调用的情况。前述情况下,onStart、onResume、onPause
为活跃状态,onchanged
会立即回调
LiveData
还提供了一个observeForever()
方法,与observe()
的区别在于当LiveData
包装的数据发生变化时,无论页面处于什么状态,observeForever()
都能收到通知。使用完成后,要记得调用removeObserver()
来停止对LiveData
的观察,否则LiveData
会一直处于激活状态,Activity
永远不会被系统自动回收。
ViewModel
中创建LiveData
对象
实际开发中一般建议LiveData
对象是存储在ViewModel对象中,主要原因有:
- 避免
Activity
和Fragment
过于庞大,让其专注于数据显示而不负责存储数据 - 将
LiveData
实例与特定的Activity
或Fragment
实例分离开,保证LiveData
对象在配置更改后继续存在
class NameViewModel : ViewModel() {
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
}
LiveData
对象观察
建议在组件的onCreate()
方法开始观察LiveData
对象,因为
- 确保系统不会从
Activity
或Fragment
的onResume()
方法进行冗余调用 - 确保
Activity
或Fragment
变为活跃状态后具有可以立即显示的数据
通常,LiveData
仅在数据发生更改时才发送更新,并且仅发送给活跃观察者。
如果观察者从非活跃状态更改为活跃状态时也会收到更新。如果观察者第二次从非活跃状态更改为活跃状态,则只有在自上次变为活跃状态以来值发生了更改时,它才会收到更新。
class NameActivity : AppCompatActivity() {
private val model: NameViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......
val nameObserver = Observer<String> { newName ->
nameTextView.text = newName
}
model.currentName.observe(this, nameObserver)
}
}
在传递 nameObserver
参数的情况下调用 observe()
) 后,系统会立即调用 onChanged()
,从而提供 mCurrentName
中存储的最新值。
如果 LiveData
对象尚未在 mCurrentName
中设置值,则不会调用 onChanged()
。
2.2 扩展LiveData
关于LiveData
,可以根据实际需要自定义扩展,复写onActive()
、onInactive()
回调方法即可。
首先看一下官方提供的一个示例
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price //监听到股价变化,
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
}
- 当
LiveData
对象具有活跃观察者时,会调用onActive()
方法 - 当
LiveData
对象没有任何活跃观察者时,会调用onInactive()
方法 -
setValue(T)
将更新LiveData
实例的值,并将更改告知活跃观察者。
至此,你可以使用StockLiveData
类
public class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val myPriceListener: LiveData<BigDecimal> = ...
myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})
}
}
LiveData
对象具有生命周期感知能力,这一事实意味着您可以在多个 Activity
、Fragment
和 Service
之间共享这些对象。你可以将LiveData
类实现为一个单例
class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
private val stockManager: StockManager = StockManager(symbol)
private val listener = { price: BigDecimal ->
value = price
}
override fun onActive() {
stockManager.requestPriceUpdates(listener)
}
override fun onInactive() {
stockManager.removeUpdates(listener)
}
companion object {
private lateinit var sInstance: StockLiveData
@MainThread
fun get(symbol: String): StockLiveData {
sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
return sInstance
}
}
}
class MyFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
// Update the UI.
})
}
只有当 存在活跃的观察者(LifecycleOwner
)时 才会连接到 股价更新服务 监听股价变化
多个 Fragment
和 Activity
可以观察 MyPriceListener
实例。仅当一个或多项系统服务可见且处于活跃状态时,LiveData
才会连接到该服务。
2.3 转换LiveData
如果想要在LiveData对象分发给观察者之前对其中存储的值进行更改,或者您可能需要根据另一个实例的值返回不同的 LiveData
实例。可以使用 Transformations
类,该类包括可应对这些情况的辅助程序方法。
Transformations.map()
该方法对存储在 LiveData
对象中的值应用函数,并将结果传播到下游。
val mutableLiveData = MutableLiveData<String>()
mutableLiveData.observe(this) {
Log.d(TAG, "onChanged:$it")
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
}
//LiveData返回值实例转换
val transformedLiveData = Transformations.map(mutableLiveData) {name ->
"${name}LiveData is great"
}
transformedLiveData.observe(this){
Log.d(TAG,"onChange2$it")
}
mutableLiveData.postValue("Hello LiveData ")
/**打印结果**/
onChanged:Hello LiveData
onChange2Hello LiveData LiveData is great
Transformations.switchMap()
与map()
类似,对存储在LiveData
对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap()
的函数必须返回 LiveData
对象。
private lateinit var mutableLiveData1:MutableLiveData<String>
private lateinit var mutableLiveData2:MutableLiveData<String>
private lateinit var liveDataSwitch:MutableLiveData<Boolean>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mutableLiveData1 = MutableLiveData()
mutableLiveData2 = MutableLiveData()
liveDataSwitch = MutableLiveData()
val transformedLiveData = Transformations.switchMap(liveDataSwitch,
Function<Boolean?, LiveData<String>> {
return@Function if (it) mutableLiveData1 else mutableLiveData2
})
transformedLiveData.observe(this){
Log.d(TAG,"onChanged:$it")
}
liveDataSwitch.postValue(false)
mutableLiveData1.postValue("LiveData init")
mutableLiveData2.postValue("LiveData developer")
}
新建一个MutableLiveData<Boolean>
来控制切换并赋值给liveDataSWitch。当liveDataSwitch
的值为true
时返回mutableLiveData1
,否则返回mutableLiveData2
。通过这种方式,达到切换监听的目的。
2.4 合并多个LiveData源
MediatorLiveData
继承自mutableLiveData
,它可以将多个LiveData
数据源集合起来,可以达到一个组件监听多个LiveData
数据变化的目的
private lateinit var mutableLiveData1:MutableLiveData<String>
private lateinit var mutableLiveData2:MutableLiveData<String>
private lateinit var liveDataManager:MediatorLiveData<String>
private lateinit var text:TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
text = findViewById(R.id.tv_text)
mutableLiveData1 = MutableLiveData()
mutableLiveData2 = MutableLiveData()
liveDataManager = MediatorLiveData()
liveDataManager.addSource(mutableLiveData1){
Log.d(TAG,"onChange1:$it")
}
liveDataManager.addSource(mutableLiveData2){
Log.d(TAG,"onChange2:$it")
}
liveDataManager.observe(this){
Log.d(TAG,"onChanged:$it")
}
mutableLiveData1.postValue("LiveData init")
mutableLiveData2.postValue("LiveData developer")
text.setOnClickListener(){
mutableLiveData1.postValue("change LiveData1 data")
}
}
/**打印结果**/
onChange1:LiveData init
onChange2:LiveData developer
onChange1:change LiveData1 data
通过MediatorLiveData
的addSource
将两个MutableLiveData
合并到一起,这样当任何一个MutableLiveData
数据发生变化时,MediatorLiveData
都可以感知到
三、原理解析
这里是基于2.4.0
版本进行原理分析。
在学习LiveData的实现原理前,先了解一下几个核心角色:
-
ObserverWrapper
Observer
的包装类,定义了LiveData
的生命周期状态监听方法 -
LifecycleBoundObserver
继承于ObserverWrapper
,并最终实现了GenericLifecycleObserver
接口 -
MediatorLiveData
继承至MutableLiveData
,可监听其他容器内容的变化,通过addSource()
- ......
看一下LiveData
的原理实现机制图,如下图所示
LiveData
的实现原理,简单总结为:
-
LiveData
的实现主要可以分为添加观察者、事件回调和事件更新三部分 - 添加观察者:将
LifecycleOwner
和observer
进行包装成可感知生命周期的包装类,添加到LifecycleObserver
中,它将在LifecycleOwner
更改状态时得到通知 - 事件回调:收到
LifecycleOwner
状态更改通知时,进行状态判断等处理,然后通知之前添加的观察者对象 - 事件更新:更新数据版本号,遍历观察者对象,进行事件分发通知
LiveData
其他补充知识:
-
LiveData
的观察者只能与一个LifecycleOwner
绑定, 否则会抛出异常。而一个owner
可以绑定多个Observer
实例 - 使用
observeForever()
方法,意味着给定的观察者将接收所有事件,并且永远不会被自动删除,不管在什么状态下都能接收到数据的更改通知,使用完注意手动移除 -
LiveData
利用数据版本管理方法,确保只会发送最新的数据,但是要注意数据倒灌
3.1 添加观察者
LiveData
通过observer()
方法来注册观察者,从该方法进行入手
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
//当前绑定的组件(activity或fragment)状态为为DESTROYED的时候,则会忽视当前的订阅请求
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
//创建生命周期感知的观察者包装类
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
//如果指定的键尚未与某个值关联,则将其与给定的值关联
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
//对应观察者只能与一个owner绑定
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
//添加一个LifecycleObserver,它将在LifecycleOwner更改状态时得到通知
owner.getLifecycle().addObserver(wrapper);
}
LiveData
的observer()
的主要内容为:
- 首先判断
LifecycleOwner
是否为DESTROYED
状态,如果是就直接忽略,不能添加。 - 其次用
LifecycleOwner
、observer
组装成LifecycleBoundObserver
包装实例wrapper
- 使用
putIfAbsent()
方法observer-wrapper
作为key-value
添加到观察者列表mObservers
中。不能添加具有不同生命周期的相同观察者,否则会抛异常,但是同owner
可以add
多个Observer
; - 最后用
LifecycleOwner
的Lifecycle
添加observer
的封装wrapper
。
3.2 事件回调
LifecycleBoundObserver
类
LiveData
通过observe()
方法添加了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() {
//至少是STARED状态
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
//关键代码
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//LifecycleOwner变成DESTROYED状态,则移除观察者
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();
}
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
LifecycleBoundObserver
类主要内容是:
- 对原始
Observer
进行包装,把LifecycleOwner
和Observer
绑定在一起。当LifecycleOwner
处于活跃状态,LifecycleBoundObserver
就是活跃的观察者。重写了shouldBeActive()
和onStateChanged()
方法。从Lifecycle
中可知,其实现了LifecycleEventObserver
接口。 - 在
LifecycleOwner
生命周期状态变化时检查,如果是DESTROYED
状态,则移除观察者。 - 如果不是
DESTROYED
状态,将调用父类ObserverWrapper
的activeStateChanged()
方法处理 这个生命周期状态变化 -
shouldBeActive()
的值作为参数,至少是STARTED
状态为true
,即活跃状态为true
。
ObserverWrapper
类
LifecycleBoundObserver
继承了ObserverWrapper
,看一下其实现
private abstract class ObserverWrapper {
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return; //活跃状态未发生改变,不会处理
}
mActive = newActive;
changeActiveCounter(mActive ? 1 : -1);
if (mActive) {
dispatchingValue(this); //观察者变为活跃,进行数据分发
}
}
......
}
@MainThread
void changeActiveCounter(int change) {
int previousActiveCount = mActiveCount;
mActiveCount += change;
if (mChangingActiveState) {
return; //如果正在变更 则返回
}
mChangingActiveState = true;
try {
while (previousActiveCount != mActiveCount) {
boolean needToCallActive = previousActiveCount == 0 && mActiveCount > 0;
boolean needToCallInactive = previousActiveCount > 0 && mActiveCount == 0;
previousActiveCount = mActiveCount;
if (needToCallActive) {
onActive(); //活跃的观察者数量 由0变为1。这是个空函数,根据需要重写
} else if (needToCallInactive) {
onInactive(); //活跃的观察者数量 由1变为0。空函数,根据需要重写
}
}
} finally {
mChangingActiveState = false;
}
}
当LifecycleOwner
状态发生改变时,会调用其activeStateChanged()
方法,根据活跃观察者数量的变化,分别调用onActive()
和onInactive()
如果状态为Active
,会调用dispatchingValue
方法,并将自身传进去。
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;
}
dispatchingValue()
主要内容是:
- 标记事件分发状态,如果正处于分发状态,表示分发无效直接返回
-
observerWrapper
不为空,就使用considerNotify()
通知真正的观察者,observerWrapper
为空 则遍历通知所有的观察者。
看一下considerNotify()
方法实现
private void considerNotify(ObserverWrapper observer) {
//观察者非活跃,直接return
if (!observer.mActive) {
return;
}
//观察者状态活跃,但是当前变为了不可见状态,再调用activeStateChanged方法,并传入false,其内部会再次判断
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//如果数据版本已经是最新的了,那么直接返回
if (observer.mLastVersion >= mVersion) {
return;
}
//修改数据版本
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);//回调真正的mObserver的onChanged方法
}
considerNotify()
方法主要内容:
- 如果
ObserverWrapper
的mActive
值不为true
,就直接return
- 如果当前
observer
对应组件的状态不是Active
,就会再次调用activeStateChanged
方法,并传入false
,其方法内部会再次判断是否执行onActive
方法和onInactive
方法回调。 - 如果判断条件都满足会调用
Observer
的onChanged
方法,这个方法正是使用LiveData
的observe
方法的回调。
considerNotify()
方法中也涉及到了相应的观察者数据版本号的比较问题
- 当页面从不可见变为可见,将
LiveData
中的数据版本号跟对应的观察者中的版本号进行比较,如果大于,则调用onChanged()
进行数据的回调。 - 如果页面为不可见,那么不会进行数据的回调处理。
所以LiveData
注册观察者后的流程为:
- 调用
observe()
注册后,绑定了LifecycleOwner
,在active
状态下,使用LiveData
中setValue
发送数据,则Observer
会立马接受到该数据修改的通知 - 状态变更后的触发流程大致为:
observe ——> onStateChanged ——> activeStateChanged ——> dispatchingValue ——> considerNotify ——> onChanged
- 在
onChanged
方法,交给外部开发者处理接收消息事件的逻辑
3.3 事件更新
在Activity
中,通过postValue
或者setValue
改变LiveData
的数据,在通过Observer
,观察LiveData
数据的变化。那么LiveData的数据更新是如何通知到Observer的?。
setValue
逻辑
LiveData
更新数据方式有两个,一个是 setValue()
, 另一个是 postValue()
。postValue()
在内部会抛到主线程去执行更新数据,因此适合在子线程中使用;而 setValue()
则是直接更新数据。
看一下setValue()
方法内部实现
@MainThread
protected void setValue(T value) {
assertMainThread("setValue"); //检查是否在主线程
mVersion++; //默认值是-1,每次更新数据都会自增
mData = value; //更新的数据赋值给mData
dispatchingValue(null); //调用 dispatchingValue()方法并传入null,将数据分发给所有观察者。dispatchingValue如果传入null则是所有的观察者,如果是具体的ObserverWrapper对象,则通知到具体的Observer。
}
-
setValue()
方法会将数据版本号+1
,并进行数据分发 - 调用
dispatchingValue()
进行事件分发。如果参数为null
且为active
状态,那么会遍历所有的监听者,逐个通知所有观察者进行了数据的变化
所以setValue
后的触发流程为:setValue
——> dispatchingValue(null)
——> considerNotify
——> onChanged
postValue
逻辑
看一下postValue()
实现
protected void postValue(T value) {
boolean postTask; //用于判断是否要更新
//加锁解决多个子线程同时调用问题
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value; //将数据存入 mPendingData
}
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);
}
};
PostValue()
是将消息通过线程池分发到主线程去处理。
四、常见问题
4.1 数据倒灌
使用LiveData
的一个常见问题是数据倒灌,表现为用户在页面没有做任何操作,却进行了页面跳转,数据请求等操作。
LiveData
的设计原则:在页面重建时,LiveData
自动推送最后一次数据,不必重新去向后台请求
对于页面重建,常见的场景有:
- 屏幕旋转
- 系统语言切换
- 内存不足,应用在后台被系统杀死。之后用户再重新进入应用
- ......
这里我们讨论的数据倒灌跟前两种场景相关。
LiveData
实例创建建议在ViewModel
中,而资源配置变更并不会导致ViewModel
实例销毁。
通过一个示例演示一下数据倒灌问题
//MainActivity.kt
private fun initObserver() {
mViewModel.testLiveData.observer(this) {
Log.i("MainActivity", "testLiveData value == $it")
Thread{
SystemClock.sleep(3000)
startActivity<SecondActivity>()
}.start()
}
}
private fun onClick(){
mBinding.btnTest.setOnClickListener { mViewModel.testLiveData.value = 3 }
}
//MainViewModel.kt
val testLiveData = MutableLiveData<Int>()
上述代码示例,如果用户在MainActivity
中点击按钮,会将ViewModel
中的testLiveData
的值置为3,然后延迟3S后会跳转到下一个页面。如果用户返回当前页面,在当前页面旋转屏幕,发现会自动跳转到下一个页面。这就是数据倒灌导致的。
前述页面旋转重建了,会自动推送最后一次数据。LiveData
的事件回调过程是:observe
——> onStateChanged
——> activeStateChanged
——> dispatchingValue
——> considerNotify
——> onChanged
。通过断点调试,可将问题定位在considerNotify
private void considerNotify(ObserverWrapper observer) {
//观察者非活跃,直接return
if (!observer.mActive) {
return;
}
//观察者状态活跃,但是当前变为了不可见状态,再调用activeStateChanged方法,并传入false,其内部会再次判断
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
//此处判断无效导致数据倒灌
if (observer.mLastVersion >= mVersion) {
return;
}
//修改数据版本
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);//回调真正的mObserver的onChanged方法
}
LiveData
在前述屏幕旋转中也执行了事件分发,其原因是if (observer.mLastVersion >= mVersion)
没有生效。
mVersion
认识
// LiveData.java
static final int START_VERSION = -1;
private int mVersion;
mVersion
是LiveData
的成员变量,一个LiveData
维护一份实例对象
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
在初始化LiveData()
时,mVersion
设置为-1,之后每次调用setValue
等方法,会执行mVersion++
自增方法。
mLastVersion
认识
mLastVersion
默认值也是-1。如果分发事件成功,将当前LiveData
的mVersion
赋值给mLastVersion
。
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
// 第一处
int mLastVersion = START_VERSION;
}
private void considerNotify(ObserverWrapper observer) {
...
// 第二处
if (observer.mLastVersion >= mVersion) {
return;
}
// 第三处
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
屏幕旋转后,该值重新变为-1,导致了数据倒灌问题发生。
回看一下LiveData
的observer()
方法
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
......
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
......
}
-
Activity
等页面重建后,LiveData
调用observe()
,方法内会new一个新的LifecycleBoundObserver
对象,该对象继承ObserverWrapper
类 -
ObserverWrapper
类初始化会重新初始化int mLastVersion = START_VERSION;
将mLastVersion
赋值为-1
- 因为
observer.mLasterVersion < mVersion
,considerNotify()
方法中的判断失效,重新分发事件,导致数据倒灌
页面重建后,会自动推送最后一次数据。为什么页面重建没有手动调用setValue()
等方法,也会触发事件分发considerNotify()
方法?
considerNotify()
方法在内部做了多重判断,如果如果当前observer
对应组件的状态不是Active
,就会再次调用activeStateChanged
方法,并传入false
,其方法内部会再次判断是否执行onActive
方法和onInactive
方法回调。
considerNotify()` ->`dispatchingValue()` -> `activeStateChanged()` -> `onStateChanged()
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//如果当前Activity的状态是onDestory,移除
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
// 上一次的state跟当前的不同时,执行事件分发
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。
简单小结:
- 页面异常销毁重建,
ViewModel
会保存销毁之前的数据,在Activity重建完成后进行数据回复,所以LiveData
成员变量中的mVersion
会恢复到重建之前的值 - 页面重建后会调用
LiveData
的observe()
方法,方法内部会重新new
一个实例,会将mLastVersion
恢复到初始值。 - 由于
LiveData
本身的特性,Activity
的生命周期由非活跃变成活跃时,LiveData
会触发事件分发,导致屏幕旋转或者切换系统语言后出现数据倒灌
数据倒灌解决方法:
4.2 postValue数据丢失
postValue
数据丢失的典型表现就是使用LiveData
时,连续postValue
两次,发现第一次的值会丢失。
viewModel.testLiveData1.postValue("hello click")
viewModel.testLiveData1.postValue("hello kotlin")
/**实际只会打印第二次postValue的值**/
testLiveData1 value == hello kotlin
看一下postValue
的内部实现
volatile Object mPendingData = NOT_SET;
/**
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value; //暂存数据,后面的数据会覆盖前面的
}
if (!postTask) { //保证只抛一个mPostValueRunnabl
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);
}
};
官方在postValue
方法的注释上也说明了这一情况。
postValue
的主要内容:
- 每次调用
postVaule
,会将新值赋给mPendingData
- 在
Runnable
中进行值的分发,通过ArchTaskExecutor
将任务发布到主线程中
到这其实连续postValue
时值会丢失的原因已经清楚了
- 调用
postValue
时,其实只是将值暂存到mPendingData
,然后往主线程抛一个Runnable
,通过setValue
将暂存的值设置进去,回调观察者 - 如果在这个
Runnable
真正执行前多次postValue
,只会改变暂存值mPendingData
,通过postTask
的检测不会再往主线程抛Runnable
作者:者文
链接:https://juejin.cn/post/7065693389554974728
如有侵权,请联系删除!
网友评论