感知生命周期的数据组件--LiveData

前言

❶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 具有生命周期感知能力,意指它遵循其他应用组件(如 activityfragmentservice)的生命周期。这种感知能力可确保 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 的组件。

如:利用 LifecycleApp进程 拥有生命周期感知 → ProcessLifecycleOwner,具体实现解读:《自定义 LifecycleOwner》

简单点说:实现了Lifecycle那一套的组件,Activity、Fragment默认实现了,所以可以直接用,具体原理解读:《【Jetpack】学穿:Lifecycle → 生命周期 (原理篇)》

② 怎么感知生命周期?

LifecycleOwner 提供了一个 getLifecycle() 用于获取 Lifecycle实例

你只需自定义一个 观察者 实现 LifecycleEventObserverDefaultLifecycleObserver,调用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 是如何接收到之前发送的数据的流程。

image.png

(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) 进行分发数据,就又回到了上面的流程。

LifeData普通消息分发数据.png

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){
	.....
}    

鸣谢

https://juejin.cn/post/7251182449400414265#heading-11