回调了 onStateChanged()
方法,符合第二种场景 → 生命周期组件状态变更:
activeStateChanged()
→ dispatchingValue()
→ considerNotify()
:
粘性的原因到此就一清二楚了,此处应有掌声!!!
怎么解决粘性问题?不急,等下讲,这里TM才开头而已,LiveData用法都还没介绍呢。
⑤ 数据驱动
简单点说:不需要主动给界面推(设置数据),界面会被动观察数据变化。
0x2、LiveData 使用详解
老规矩,官方文档双手奉上:《LiveData 概览》,以官方文档和源码为准~
① 依赖组件
LiveData基本配着ViewModel一起用的,你实在不想依赖,可以按官方文档的来:Lifecycle
def lifecycle_version = 2.4.1 // Java项目 implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version" // Kotlin项目 implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
咳,如果你启用了DataBinding,可以不用另外依赖,不然你会发现两个版本的livedata:
命令行键入:gradlew :app:dependencies > dependencies.txt
扫一波就知道了:
② 创建、观察、更新
创建就不用说了,LiveData是抽象类,要么继承自定义,要么用 MutableLiveData
。
观察、更新在粘性那里的例子已经演示得很清楚了,就不Copy了,这里提一嘴 添加观察者的两个方法:
observe()
→ 当生命周期组件处于STARTED和RESUMED状态下才会收到分发值。observeForever()
→ 跟生命周期组件状态没关系了,都会收到分发值,要手动调removeObserver()
移除!
还有 两种改变数据的方法:
setValue()
→ 只能在主线程中调用;postValue()
→ 既可以在主线程中调用,也可以在子线程中调用,最终调用的还是setValue();
③ 为什么LiveData搭配ViewModel使用?
- 1、避免Activity、Fragment的代码臃肿,不塞ViewModel里也要做数据和组件分离;
- 2、将LiveData与特定的Activity、Fragment分离,配置发生更改(如旋转)导致重建,不影响LiveData。
④ LiveData扩展
就是LiveData预留了两个回调:onActive()
和 onInactive()
,当生命周期组件 在活跃和非活跃状态间切换 时会回调。
可以怎么玩?比如写一个全局可用的倒计时器,先写倒计时相关功能:
object CountDownManager { private var mRemainSecond: Long = 10L private var mTimer: CountDownTimer? = null private var mListener = arrayListOf<CountDataChangeListener>() // 开始倒计时 fun startCount(remainSecond: Long? = 10L) { mRemainSecond = remainSecond!! mTimer = object: CountDownTimer(remainSecond * 1000, 1000) { override fun onTick(millisUntilFinished: Long) { mRemainSecond-- dispatchMessage("剩余:$mRemainSecond 秒") } override fun onFinish() { dispatchMessage("倒计时结束") } } mTimer!!.start() } // 取消倒计时 fun cancelCount() { if(mTimer != null) { mTimer!!.cancel() mListener.clear() } } // 遍历回调方法 private fun dispatchMessage(msg: String) { mListener.forEach { it.onChange(msg) } } // 添加监听器 fun setListener(listener: CountDataChangeListener) { mListener.add(listener) } // 移除监听器 fun removeListener(listener: CountDataChangeListener) { mListener.remove(listener) } } // 回调接口 interface CountDataChangeListener{ fun onChange(msg: String) }
非常简单,就是初始化倒计实例,遍历回调而已,接着继承LiveData,写一个全局数据类:
class GlobalLiveData : LiveData<String>() { companion object { private lateinit var instance: GlobalLiveData fun getInstance() = if (::instance.isInitialized) instance else GlobalLiveData() } private val mListener = object : CountDataChangeListener { override fun onChange(msg: String) { postValue(msg) // 调用更新数据的方法 } } fun startCount(remainSecond: Long? = 10L) { CountDownManager.startCount(remainSecond) } fun cancelCount() { CountDownManager.cancelCount() } // 活跃时添加监听器 override fun onActive() { super.onActive() CountDownManager.setListener(mListener) } // 不活跃时移除监听器 override fun onInactive() { super.onInactive() CountDownManager.removeListener(mListener) } }
然后是调用处:
// 添加倒计时监听 GlobalLiveData.getInstance().observe(this) { mBinding.tvTest.text = it } // 开始倒计时 GlobalLiveData.getInstance().startCount(30)
你只管添加观察,无需担心移除,页面 (Activity、Fragment) 不活跃时不更新页面,活跃时才更新。运行效果如下:
Tips:只是写下例子,真正用到项目还得自己改下哈,如添加/移除观察者时加锁 ~
⑤ LiveData转换
Lifecycle包提供了 Transformations
来对LiveData的数据类型进行转换,可以 在数据返回给观察者前,修改LiveData中数据的具体类型。
Transformations
中有两个常用的转换方法 map()
和 swtichMap()
,它们都是使用 MediatorLiveData
作为数据的中间消费者,并将转换后的数据传递给最终消费者。
Transformations.map()
先看下map()的具体实现:
流程解读:
- 实例化一个
MediatorLiveData
实例 result; - 调用
addSource()
传入源LiveData
和新建的Observer实例
; - 在Observer的回调方法onChange()中,把 转换函数的执行结果 传入result的setValue()方法中;
- 最后返回result;
跟下:MediatorLiveData.addSource()
:
用Source包了一层,将传入参数关林,直接跟 Source.plug()