前言
❶LiveData,原意是 活着的数据。 数据还能有生命?
LiveData 组件是 Jetpack 新推出的基于观察者的消息订阅/分发组件,具有宿主(Activity/Fragment)生命周期感知能力。这种感知能力可确保 LiveData 仅分发消息给与活跃状态
的观察者,即只有处于活跃状态
的观察者才能收到消息。
❷LiveData由于Handler EventBus
LiveData 的消息分发机制,是以往 Handler,EventBus,RxjavaBus 无法比拟的,它们(Handler,EventBus,RxjavaBus)不会顾及当前页面是否可见,一股脑的有消息就转发。导致即便应用在后台,页面不可见,还在做一些无用的绘制,计算(微信的消息列表是在可见状态下才会更新列表最新信息的)将有限的资源让给可见的页面使用。
❸LiveData核心思想
基于生命周期,其实就是在当前宿主(Activity/Fragment)的 LifecycleOnwer 注册一个 Observer观察者,那么宿主(Activity/Fragment)每次生命周期的变化,都会回调给Observer观察者的
onStateChange()
方法,即便是刚刚注册的Observer观察者宿主也会回调onStateChange()
方法,会有一个状态同步的过程,LiveData 也是利用这个能力,巧妙实现了当宿主(Activity/Fragment)销毁的时候,自动移除注册进来的 Observer观察者,从而避免了手动移除的麻烦。更不会造成内存泄漏,这个也是它的核心思想。
官方的定义:
LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,
LiveData
具有生命周期感知能力,意指它遵循其他应用组件(如activity
、fragment
或service
)的生命周期。这种感知能力可确保LiveData
仅更新处于活跃生命周期状态的应用组件观察者。说简单就是
LiveData
是一个可观察的数据存储类,内部借助了Lifecycle
,从而实现了生命周期感知,同时,这个可观察指的是,在其存储的数据更新时,它会去通知观察者。又因为生命周期感知的存在,所以可以做到 何时通知、何时解绑,从而做到安全无泄漏,就是如此:)
LiveData特点
LiveData通常在ViewModel里面创建,这是因为以下原因:
❶确保系统不会从 Activity 或 Fragment 的
[onResume()]
方法进行冗余调用。❷确保 宿主(Activity/Fragment)变为活跃状态后具有可以立即显示的数据。一旦应用组件处于 STARTED 状态,就会从它正在观察的
LiveData
对象接收最新值。只有在设置了要观察的LiveData
对象时,才会发生这种情况。❸不会发生内存泄漏, 观察者observer会绑定到 Lifecycle 对象(LifecycleOwner),并在LifecycleOwner关联的生命周期遭到销毁后进行自我清理(DESTROYED后自动remove)。
❹不会因 Activity 停止而导致崩溃,如果LifecycleOwner(Activity)生命周期处于非活跃状态,则它不会接收任何 LiveData事件。
❺不需要手动解除观察,开发者不需要在onPause或onDestroy方法中解除对LiveData的观察,因为LiveData能感知生命周期状态变化,所以会自动管理所有这些操作。
❻数据始终保持最新状态,数据更新时 若LifecycleOwner为非活跃状态,那么会在变为活跃时接收最新数据。例如,曾经在后台的 Activity 会在返回前台后,observer立即接收最新的数据。
❼适当的配置更改:如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
❽共享资源:您可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。LiveData 对象连接到系统服务一次,然后需要相应资源的任何观察者只需观察 LiveData 对象。
① 感知谁的生命周期?
具有生命周期感知的组件,一般代指Activity、Fragment,但不局限此,更泛指实现了 Lifecycle.LifecycleOwner 的组件。
如:利用 Lifecycle 让 App进程 拥有生命周期感知 → ProcessLifecycleOwner,具体实现解读:《自定义 LifecycleOwner》
简单点说:实现了Lifecycle那一套的组件,Activity、Fragment默认实现了,所以可以直接用,具体原理解读:《【Jetpack】学穿:Lifecycle → 生命周期 (原理篇)》
② 怎么感知生命周期?
LifecycleOwner
提供了一个getLifecycle()
用于获取Lifecycle实例
;你只需自定义一个 观察者 实现
LifecycleEventObserver
或DefaultLifecycleObserver
,调用Lifecycle实例的addObserve()
添加观察者实例即可。当组件生命周期发生变化,就会通知所有的观察者(回调方法)。
LiveData里,还对观察者包了一层 → LifecycleBoundObserver
图中圈住的①:当组件处于 DESTORYED 状态时,自动移除观察者,这样的好处:避免内存泄露。
图中圈住的②,点开
activeStateChanged()
方法:当组件处于 STARTED/RESUMED 状态,才发送数据,这样的好处:避免因组件处于非活跃状态,接收LiveData事件导致的崩溃,最常见的就是Activity停止了,还执行相关回调。
③ 可观察
除了 LifecycleBoundObserver 生命周期观察者外,LiveData内部还提供了一个 数据观察者 Observer
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}
在Activity里通过getFaceDetectionQRCodeSucLiveData实例对象,可以调用 observe()方法添加数据观察者对象:
class OTCBannerActivity : BaseBindingActivity<ActivityOtcBannerBinding>() {
initView(){
getFaceDetectionQRCodeSucLiveData
.observe(owner=currentActivity,
onChanged={ onFaceDetectionQRCodeSuc(it) })
}
}
在LiveData.kt源码里的observe()方法里 创建了一个观察者对象wrappedObserver:Observer
然后调用LiveData.java的 observe(owner, wrappedObserver)方法 来添加数据观察者对象wrappedObserver:
在LiveData.java的 observe(LifecycleOwner owner, Observer<? super T> observer)方法里传入观察者对象wrappedObserver
在 dispatchingValue()
分发事件,considerNotify()
中回调 onChanged()
,完成数据更新。
④ 粘性
所谓的 粘性,通俗点说: LiveData 先先发送数据,后注册的新 Observer观察者对象后后进行订阅,也可以收到之前发的数据。
在LiveData中的表现:后面新的观察者注册时,会收到之前注册分发的值。
粘性事件不支持取消(后面注册的观察者也能接收数据,无法反注册,但有办法解决)。
⑤ 数据驱动
简单点说:不需要主动给界面推(设置数据),界面会被动观察数据变化。
LiveData核心方法
方法名 | 作用 |
observe(LifecycleOwner owner, Observer observer) | 注册和宿主(Activity/Fragment)生命周期关联的观察者observer, owner当前生命周期的宿主(Activity/Fragment)。 判断当前宿主(Activity/Fragment)的状态是否为 DESTORYED,如果是(宿主销毁了)则主动进行反注册,把 Observer 观察者移除掉。从而主动避免内存泄漏的问题。 |
observeForever(Observer observer) | 注册观察者observer,不会反注册,需自行维护,没有owner无法管理宿主生命周期.不管你的宿主是否处于可见状态,这就意味着它可以一直接收数据。 |
setValue(T value) | 发送数据,没有活跃的观察者时不分发,只能在主线程 |
postValue(T value) | setValue一样,但是不受线程限制,内部也是通过handelr.post到主线程,最后还是通过setValue来分发的 |
onActive() | 当且仅当有一个活跃的观察者observer时会触发 |
onInactive() | 不存在活跃的观察者observer时会触发 |
LiveData实现原理
(1)粘性事件分发流程
❶通过 observe(owner,observer)
向 LiveData 注册观察者,并且把 观察者对象observer 包装成一个 LifecycleBoundObserver
,它是一个具有生命周期边界的观察者,因为这个观察者只有当宿主处于 STARTED 或者 RESUMED 状态的它才会接收数据,其他时候它是不会接收数据的。
❷把包装好的观察者对象 Observer 注册到 Lifecycle 当中,handlerLifecycleEvent(event)
利用 Lifecycle 能力,它能感知宿主生命周期能力的关键地方。注册时和宿主每次生命周期变化都会回调 onStateChanged()
方法,刚进去的时候会触发方法的同步。
❸会判断这个事件宿主是否被销毁了,从而主动地把 Observer 从 LiveData 中移除掉,流程结束。如果不是 DESTORY,说明宿主当前的状态发生了变化,它会触发 activeStateChanged(boolean newActive)
方法,它会判断当前 Observer 是否处于活跃的状态,如果宿主的状态为 STARTED,RESUMED 则会分发最新数据到每个观察者。
❹进而调用 dispatchingValue(ObserverWrapper)
分发数据,如果 ObserverWrapper 为空则分发数据给 liveData 中存储的所有观察者,如果不为空,则分发数据给该 Observer。
❺considerNotify(ObserverWrapper)
中先判断观察者所在的宿主不活跃,则不分发;接着如果 observer 的 mLastVersion 大于或等于 LiveData 的 mVersion 则不分发,防止重复发送数据;最后通过 observer.mObserver.onChanged((T) mData)
分发数据,同步 mVersion 数据。
重点来了,LiveData 的 Version 字段和 Observer 的 Vierson 在刚开始创建的时候都是-1,如果 LiveData 已经发送数据了,它的 Version 字段就会加1,如果这个时候新注册了一个 Observer,那么在触发消息分发的时候,这两个字段就不相等,所以 Observer 就能接收到之前发送的消息,在第一次注册的数据的时候(先发数据,后注册的 Observer 也会收到数据)。这就是粘性事件,目的是为了避免数据多次重复发送,因为每次生命周期的变化都会走到这里。最后调用
observer.mObserver.onChanged()
回调数据。
这就是一个新的注册的 Observer 是如何接收到之前发送的数据的流程。
(2)普通消息分发流程
普通消息分发流程即调用 postValue()
,setValue()
才会触发消息的分发。
❶
postValue()
发送一条数据,它可以在任意线程使用的,里面实际使用了 Handler.post 先把这个事件发送到主线程,然后在调用setValue()
发送数据;❷
setValue()
代表着 LiveData 发送数据,每发送一次 mVersion++,另外LifecycleBoundObserver
中也有一个,它代表这个 Observer 接收了几次数据,在分发数据的时候,这两个 version 会进行比对,防止数据重复发送;❸
setValue()
里面也会触发dispatchingValue(ObserverWrapper)
,ObserverWrapper 为 null,dispatchingValue()
它会遍历 Observer 集合里面所有观察者,然后逐一调用considerNotify(ObserverWrapper)
去做消息的分发。
① postValue()
发送数据的流程:不限制线程,主线程,子线程都可以调用
因为需要 Handler 把这个消息 post 到主线程里面,所以需要把传递进来的 value 保存 mPendingData,当这条消息被执行的时候,就会触发 mPostValueRunnable,里面实际也是调用 setValue()
,把上面存储的 mPendingData 传递进去。
无论是从哪里发送的数据,接收的地方始终都会发送在主线程,每发送一条消息 mVersion 增加了1,在消息派发的时候就会和 Observer 的 version 进行对比,防止消息重复发送的问题。
就会调用 dispatchingValue(null)
传递了 null,根据上面的分析,如果参数为 null,就会遍历 mObserver 集合中的观察者去逐一判断是否能把数据分发给他们。
#LiveData.java
// 不限制线程,主线程,子线程都可以调用
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() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
// 执行setValue()
setValue((T) newValue);
}
};
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
// 分发数据
dispatchingValue(null);
}
②setValue()
里面调用 dispatchingValue(null)
进行分发数据,就又回到了上面的流程。
LiveData 使用详解
① 依赖组件
LiveData基本配着ViewModel一起用的
// livedata给Java项目使用(已经废弃)
implementation "androidx.lifecycle:lifecycle-livedata:2.6.1"
// livedata给Kotlin项目使用
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.6.1"
② 创建、观察、更新
- 创建LiveData实例,指定源数据类型
- 创建Observer实例,实现onChanged()方法,用于接收源数据变化并刷新UI
- LiveData实例使用observe()方法添加观察者,并传入LifecycleOwner
- LiveData实例使用setValue()/postValue()更新源数据 (子线程要postValue())
创建就不用说了,LiveData是抽象类,要么继承自定义,要么用 MutableLiveData
。
添加观察者的两个方法:
observe()
→ 当生命周期组件处于STARTED和RESUMED状态下才会收到分发值。observeForever()
→ 跟生命周期组件状态没关系了,都会收到分发值,要手动调removeObserver()
移除!
两种改变数据的方法:
setValue()
→ 只能在主线程中调用;postValue()
→ 既可以在主线程中调用,也可以在子线程中调用,最终调用的还是setValue();
③ 为什么LiveData搭配ViewModel使用?
- 1、避免Activity、Fragment的代码臃肿,不塞ViewModel里也要做数据和组件分离;
- 2、将LiveData与特定的Activity、Fragment分离,配置发生更改(如旋转)导致重建,不影响LiveData。
在ViewModel里创建LiveData
class NameViewModel : ViewModel() {
// Create a LiveData with a String
val mLiveData: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
在activity/fragment里观察
class NameActivity : AppCompatActivity() {
private val mViewModel: NameViewModel by viewModels()
private lateinit var otcModelManager: OTCModelManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
otcModelManager = SingleInstanceProvider.get(OTCModelManager::class.java)
val nameObserver=Observer<String>{newName->
nameTextView.text = newName
}
mViewModel.mLiveData.observe(this,nameObserver)
otcModelManager.getFaceDetectionQRCodeData(mViewModel.mLiveData)
}
}
class OTCModelManager() {
fun getFaceDetectionQRCodeData(mLiveData: MutableLiveData<String>){
mLiveData.value = anotherName //该方式不可以在子线程使用
//or
mLiveData.postValue(anotherName) //该方式可以在子线程使用
}
}
④ LiveData扩展
就是LiveData预留了两个回调:onActive()
和 onInactive()
,当生命周期组件 在活跃和非活跃状态间切换 时会回调。
⑤MutableLiveData
在使用 LiveData 做消息分发的时候,需要使用这个子类,设计的原因是考虑到单一开闭原则,只有拿到 MutableLiveData 才可以发送消息,LiveData 只能接收消息,避免拿到 LiveData 既能发送消息又能接收消息的混乱使用。
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}
MutableLiveData 仅仅是把上面两个方法从父类LiveData 的 protect 改为 public 而已,因为在 父类LiveData 中无法调用 postValue()
和 setValue()
。所以我们在使用 LiveData 做消息分发的时候,我们需要使用它的子类 MutableLiveData进行消息分发。
class LiveDataViewModel : ViewModel(){
val userLiveData = MutableLiveData<String>()
fun getUserInfo() {
// 模拟请求接口返回数据
viewModelScope.launch(context=Dispatchers.IO,
block={
delay(1000)
userLiveData.postValue("苏火火 苏火火 苏火火 苏火火 苏火火")
})
}
}
class MainActivity : AppCompatActivity() {
val TAG = "MainActivity"
@SuppressLint("MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val liveDataViewModel = ViewModelProvider(this).get(LiveDataViewModel::class.java)
liveDataViewModel.getUserInfo()
//注册监听,监听数据的回调
val userLiveData:LiveData<String> = liveDataViewModel.userLiveData
userLiveData.observe(this, Observer {
//接收到数据
Log.e(TAG,"接收到数据=${it}")
})
}
}
⑤ LiveData数据转换
在Lifecycle 软件包会提供 Transformations 类对LiveData的数据类型进行转换,可以 在数据返回给观察者前,修改LiveData中数据的具体类型。
Transformations
中有两个常用的转换方法 map(
LiveData
)
和 swtichMap(
LiveData
)
,它们都是使用 MediatorLiveData
作为数据的中间消费者,并将转换后的数据传递给最终消费者。
❶ Transformations.map()数据转换
结论:每次源LiveData数据发生变化,转换后的LiveData数据也跟着变化。
Transformations.map
可以对 LiveData 的数据在分发给观察者之前进行转换,并且返回一个新的 LiveData 对象。<X, Y> LiveData<Y> map(LiveData<X> source,Function<X, Y> mapFunction)
的参数mapFunction(在kotlin里使用lambda表达式实现)的
返回普通类型Y
先看下map()的具体实现:
流程解读:
•实例化一个 MediatorLiveData
实例 result对象;
•调用 addSource()
传入 源LiveData(即source)
和 新建的new Observer()实例对象
;
•在new Observer()的回调方法onChange()中,把 转换函数的执行结果 传入result的setValue()方法中;
•最后返回result对象;
跟下:MediatorLiveData.addSource()函数源码
:
用Source包了一层,将传入参数关林,直接跟 Source.plug()
吼,就是给 源LiveData (即source)
添加了一个观察者,当它发生数据变化时更新 新LiveData 的值。
map转换案例1:
val getFaceDetectionQRCodeSucLiveData:MutableLiveData<FaceDetectionQRCodeBean>
by lazy {MutableLiveData<FaceDetectionQRCodeBean>() }
val stringLiveData : LiveData<String> = Transformations.map(getFaceDetectionQRCodeSucLiveData) { bean ->
"${bean.merchantName}"
}
stringLiveData.observe(owner=reference?.get()!!, onChanged = {
LogUtil.e(TAG, "输出 stringLiveData 数据=${it}") //输出 stringLiveData 数据=ai全球鹰
})
map转换案例2:
// 源LiveData
val mUserLiveData = MutableLiveData<User>()
mUserLiveData.value=User(name="default", age=28)
// 查看源LiveData 默认原数据
mUserLiveData.observe(this) {user:User->
Log.e(TAG, "查看源mUserLiveData更新: ${user.toString()}") // 查看源mUserLiveData更新: User:name=default, age=28
}
//转换 源LiveData 数据类型 返回一个新的LiveData 数据类型
val mNewUserLiveData : LiveData<Int> = Transformations.map(mUserLiveData) { user:User->
user.age //todo 这里返回 普通类型 Int
}
// 查看新的LiveData 数据变化
mNewUserLiveData.observe(this) { age: Int ->
Log.e(TAG, "查看新的mNewUserLiveData数据更新了:$age") //查看新的mNewUserLiveData数据更新了:28
}
Transformations.switchMap()数据转换
结论:仅把源LiveData作为触发器,执行传入函数后返回新的LiveData,源LiveData数据发生变化不影响转换后的LiveData数据。
<X, Y> LiveData<Y> switchMap(LiveData<X> source,Function<X, LiveData<Y>> switchMapFunction)
的参数switchMapFunction(在kotlin里使用lambda表达式实现)
返回LiveData
类型LiveData<Y>。
public static <X, Y> LiveData<Y> switchMap(
LiveData<X> source,
final Function<X, LiveData<Y>> switchMapFunction) {...}
switchMap案例1:
val getFaceDetectionQRCodeSucLiveData:MutableLiveData<FaceDetectionQRCodeBean>
by lazy { MutableLiveData<FaceDetectionQRCodeBean>() }
val getJGPushReceiverLiveData: MutableLiveData<String?>
by lazy{ MutableLiveData<String?>() }
val resultLiveData: LiveData<String?> = Transformations.switchMap(getFaceDetectionQRCodeSucLiveData) {bean->
getJGPushReceiverLiveData.value=bean.merchantLogo
getJGPushReceiverLiveData//lambda的最后一行要返回一个 LiveData对象
}
resultLiveData.observe(owner=reference?.get()!!, onChanged = {
LogUtil.e(TAG, "输出 resultLiveData 数据=${it}") //输出 resultLiveData 数据=https://test-consumer.aiyzy.com//file/image/1580808861459533825
})
switchMap案例2:
// 源LiveData
val mUserLiveData2 = MutableLiveData<User>()
mUserLiveData2.value=User(name="default2", age=25)
// 查看源LiveData 默认原数据
mUserLiveData2.observe(this) {user:User->
Log.e(TAG, "查看源mUserLiveData2更新: ${user.toString()}") // 查看源mUserLiveData2更新: User:name=default2, age=25
}
//todo 这里返回 LiveData<Int>类型
val mNewUserLiveData2 : LiveData<Int> = Transformations.switchMap(mUserLiveData2,{user:User->
val newLiveData :MutableLiveData<Int> = MutableLiveData<Int> ()
newLiveData.value=user.age
return@switchMap newLiveData //todo 这里返回MutableLiveData<Int>类型
})
// 查看新的LiveData 数据变化
mNewUserLiveData2.observe(this){age: Int ->
Log.e(TAG, "查看新的mNewUserLiveData2数据更新了:$age") //查看新的mNewUserLiveData2数据更新了:25
}
⑥ MediatorLiveData:合并多个LiveData源
场景:页面有多个数据源,单独用LiveData的话,每个都要定义一个observer,很繁琐。
合并数据源,非常适合存在多个 关联 网络请求或数据库查询的场景。
解法:可以利用 MediatorLiveData 来合并多个数据源,只需定义一个observer。
MediatorLiveData 本身也是LiveData,有它自己的value,你得通过setValue()去修改它的值,使用observe()才会触发数据更新回调~
MediatorLiveData 继承自 MutableLiveData,它可以同一观察多个 LiveData 的发射的数据进行统一处理,同时也可以做为一个 LiveData,被其他 Observer 观察。
fun mediatorLiveData() {
val liveData1 = MutableLiveData<String>()
val liveData2 = MutableLiveData<String>()
//在创建一个聚合类MediatorLiveData
val mediatorLiveData = MediatorLiveData<String>()
//分别把LiveData合并到mediatorLiveData中
mediatorLiveData.addSource(liveData1, Observer { data ->
mediatorLiveData.value = data
})
mediatorLiveData.addSource(liveData2, Observer { data ->
mediatorLiveData.value = data
})
//数据监听,一旦liveData1或者LiveData2发送了数据,observer便能观察到,以便统一处理更新
mediatorLiveData.observe(this, Observer { data ->
LogUtil.e("mediatorLiveData:$data")
})
// 模拟发送数据
liveData1.postValue("liveData1 苏火火苏火火")
liveData2.postValue("liveData2 苏火火苏火火")
}
使用LiveData打造消息总线(这个先暂时不看 等后面有时间了在研究)
基于 LiveData 打造一款不会内存泄漏不用反注册的消息总线,且支持粘性事件(后注册的 新观察者对象Observer 也会收到之前的发送的数据)。
https://juejin.cn/post/7251182449400414265#heading-11
LiveData 常见问题及解决
① 解决LiveData带来的粘性问题
上面说过粘性问题 → 添加新观察者,收到之前分发的值,原因如下:
LiveData没有像EventBus一样,区分粘性和非粘性事件,也没有提供开关,在需要非粘性的场景,就得我们自己想办法了。
方案一:反射干涉Version (无效 pass)
方案二:引入中间层 (无效 pass)
方案三:拦截观察者回调(google官方给出的解决方案 但是依然无效 pass)
方案四:不用LiveData,改用Kotlin-Flow
今年的谷歌I/O大会,Yigit 在Jetpack的AMA中明确指出Livedata的存在就是为了照顾Java的使用者,短期内会继续维护。作为Livedata的替代品Flow会在今后渐渐成为主流,用上Flow,就不存在粘性问题了。
② LiveData会丢失数据吗?
在高频数据更新的场景下使用 LiveData.postValue() 时,会造成数据丢失。 因为 设值 和 分发值 是分开执行的,存在延迟。值先被缓存在变量中,再向主线程抛一个分发值的任务。在这个延迟期间,再调用一次postValue(),变量中缓存的值被更新了,会导致之前的值在未分发前就被擦除。
③ lambda优化隐藏的坑
具体探究过程可以看下:《奇怪的编译优化》,直接说结论:
lambda写法,编译器在编译时会自作聪明优化成 添加的同一个静态的观察者。
private void test3() {
for (int i = 0;i < 10; i++) {
model.getCurrentName().observe(this, s -> Log.v("ttt", "s:" + s))
}
}
上述代码值发生改变,并不会收到10条通知,只会收到1条,引入外部变量 可以绕过这个优化。
④ Fragment中使用LiveData的注意事项
Fragment和其中的View生命周期不完全一致,观察LiveData时用 viewLifecycleOwner
而不是直接用 this
。
⑤ LiveData的数据抖动问题
所谓的数据抖动:LiveData的setValue()不会判断值是否与旧值相等,都会回调Observer.onChange()。
可以通过扩展 Transformations.kt
中的 distinctUntilChanged()
方法来解决,代码示例如下:
val nNum = MediatorLiveData <Int>()
findViewById<Button>(R.id.bt_livedata_doudong_test).setOnClickListener { nNum.value = 100 }
nNum.observe(this) {Log.e(TAG , "观察者(抖动):${nNum.value!!}") }
// 此时疯狂点击按钮,不断输出很多遍重复的数据: 观察者(抖动):100 观察者(抖动):100 观察者(抖动):100 ....
val nMediatorData = Transformations.distinctUntilChanged(nNum)
nMediatorData.observe(this) {Log.e(TAG, "观察者(非抖动):${nNum.value!!}") }
//此时疯狂点击按钮,只输出一遍数据: 观察者(非抖动):100
标志位mFiestTime,第一次肯定进,非第一次判断新旧值是否相等
抛Cannot add the same observer with different lifecycles的问题
如果一个activity,在onCreate的时候建立Livedata监听,当此activity启动两遍的时候,会抛出Cannot add the same observer with different lifecycles异常,原因是使用了lamda表达式。
使用lamda表达式建立监听,当Activity未finish的时候,再一次启动的时候,就会报异常,
原因是在同一个类里 使用lamda表达式 实例化的对象,引用外部的静态方法
或者静态引用
,导致是同一个实例。
public class MyActivity extends AppCompatActivity {
static String TEST_NAME = "HAHHA";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int num = 1;
liveData.observe(this, value-> {
// 内部新建实例
Gson gson = new Gson;
// 只是使用了外部的基本类型
int thisNum = num;
// 调用外部的静态方法和静态引用
show(TEST_NAME );
});
}
static void show(String name){
.....
}
}
如何避免这个异常呢?
引用了外部的非静态引用
或者非静态的方法
,两个相同的activity下,就不是同一个实例,就不会报这个异常错误了。
String EXTAR_NAME = "HAHHA";
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final int num = 1;
liveData.observe(this, value-> {
Gson gson = new Gson;
// 调用外部静态引用
String str = EXTAR_NAME;
// 调用外部非静态方法
show(EXTAR_NAME);
});
}
}
void show(String name){
.....
}