LiveData 组件
什么是LiveData
LiveData组件是Jetpack推出的基本观察者模式的消息订阅和分发组件,具有宿主(Activity/Fragment)生命感知能力,这种感知能力,可确保LiveData暂时只分发给处于活跃状态的观察者
活跃状态:通常情况下等于Observer处于started,resumed状态,当然如果注册观察者时使用observerForever时,则该Observer则一直处于活跃状态
LiveData只分发给处于活跃状态的Observer,既保证了活跃的Observer数据和界面及时更新,又省去了处在不活跃状态Observer在后台刷新数据和界面占用的资源. LiveData的出现解决了以往回调方式带来的空指针,后台抢占资源,手动反注册等问题
LiveData优势
-
确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 [Observer]对象并把最新数据派发给它。观察者可以在收到onChanged事件时更新界面,而不是在每次数据发生更改时立即更新界面。
-
不再需要手动处理生命周期
只需要观察相关数据,不用手动停止或恢复观察。LiveData 会自动管理Observer的反注册,因为它能感知宿主生命周期的变化,并在宿主生命周期的onDestory自动进行反注册。因此使用LiveData做消息分发不会发生内存泄漏
-
数据始终保持最新状态
如果宿主的生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
-
支持黏性事件的分发
即先发送一条数据,后注册一个观察者,默认是能够收到之前发送的那条数据的
-
共享资源
我们可以使用单例模式拓展 LiveData,实现全局的消息分发总线。
LiveData的使用
简单使用
LiveData 是抽象类,它有一个简单的实现类MutableLiveData
class LiveDataActivity : AppCompatActivity() {
private val myHandler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
testLiveData1()
}
//创建LiveData对象
private val myLiveData = MutableLiveData<String>()
private fun testLiveData1() {
//添加观察者
myLiveData.observe(this, {
Log.i("LiveDataActivityTag",it)
})
//使用postValue 发送值
myLiveData.postValue("My------------")
myHandler.postDelayed({
//延迟3s后使用setValue设置值
myLiveData.value = "My++++++++++++++++++"
}, 3000)
}
}
结果(注意时间)
接下来测试一下不活跃状态下发送更改值的效果,打开界面后立马按下Home键停留超过3s后回来
结果(注意时间)
这也验证了LiveData只是暂时不发分给处于不活跃状态的Observer,当该Observer重新回到活跃状态时立马又给该Observer发送消息及时更新数据
LiveData.setValue() 该方法调用必须在主线程中
LiveData.postValue() 该方法可在子线程中调用也可在主线程调用
LiveData的操作符使用
Transformations.map()使用
如果想在LiveData分发数据之前对数据进行更改可使用该操作符
class LiveDataActivity : AppCompatActivity() {
private val myHandler = Handler()
private val myLiveData = MutableLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
testMap()
}
private fun testMap() {
myLiveData.observe(this, Observer {
//用于观察元数据
Log.i("LiveDataActivityTag", it)
})
//map转换之后会返回对应泛型的LiveData对象
val map = Transformations.map(myLiveData, Function {
//对结果拼接了几个美元符号
"$it$$$$$$$$$$"
})
//监听转换后的LiveData
map.observe(this, Observer {
Log.i("LiveDataActivityTag", it)
})
//发送值
myLiveData.postValue("My")
//延迟3s后发送另一个值
myHandler.postDelayed({
myLiveData.postValue("You")
}, 3000)
}
}
结果:
跟上面例子一样,在处于不活跃状态下也是不会收到数据的,这里不再演示
Transformations.switchMap()使用
当你需要根据某种条件切换观察不同的LiveData时可使用该操作符
class LiveDataActivity : AppCompatActivity() {
//定义三个LiveData,其中myLiveData3作为切换条件
private val myLiveData = MutableLiveData<String>()
private val myLiveData2 = MutableLiveData<String>()
private val myLiveData3 = MutableLiveData<Boolean>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
testSwitchMap()
}
/**
kotlin接口写法总结:
一个原则,三个优化:
原则:
接口作为参数时参数位置替换为此写法:{参数1,参数2 ... -> {需要运行的方法}}
优化:
1.当接口做参数位于最后一个参数时,可以提到小括号外面
2.如果是唯一参数,甚至可以省略小括号
3.当接口的回调方法参数只有一个时,参数和函数推导符可以省略,默认it为其参数
* */
private fun testSwitchMap() {
//以myLiveData3作为切换条件 进行不同的返回
val switchMap = Transformations.switchMap(myLiveData3) { input ->
if (input)
myLiveData
else
myLiveData2
}
//观察该数据变化
switchMap.observe(this, Observer {
Log.i("LiveDataActivityTag", it)
})
//先发动切换条件
myLiveData3.postValue(true)
//两个LiveData分别发送数据
myLiveData.postValue("My+++++++")
myLiveData2.postValue("You+++++++")
}
}
因为作为条件的LiveData 数据为true 切换到了第一个LiveData,所以只能接受到第一个的数据变化,第二个没有接收到
MediatorLiveData.addSource()合并多个LiveData
当需要同时对多个LiveData进行监听时,则该类可实现
class LiveDataActivity : AppCompatActivity() {
private val myLiveData = MutableLiveData<String>()
private val myLiveData2 = MutableLiveData<String>()
private val myLiveData3 = MutableLiveData<Boolean>()
private val myLiveData4 = MediatorLiveData<Any>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
testMediatorLiveData()
}
private fun testMediatorLiveData() {
myLiveData4.addSource(myLiveData, Observer {
Log.i("LiveTag1", it)
})
myLiveData4.addSource(myLiveData2, Observer {
Log.i("LiveTag2", it)
})
myLiveData4.addSource(myLiveData3, Observer {
Log.i("LiveTag3", it.toString())
})
myLiveData4.observe(this, Observer {
Log.i("LiveTag4", it.toString())
})
myLiveData.postValue("My+++++++")
myLiveData2.postValue("You+++++++")
myLiveData3.postValue(true)
myLiveData4.postValue("He+++++++++++")
}
}
结果
虽然 MediatorLiveData 可以将多个LiveData合并监听,但是值的变化只在对应的Observer中,这里可定义一个类统一接受或者分别进行处理
LiveDate的缺点???
短时间内多次调用LiveData.postValue()
先看个有趣的现象
class LiveDataActivity : AppCompatActivity() {
private val myHandler = Handler()
private val myLiveData = MutableLiveData<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_live_data)
testStick()
}
private fun testStick(){
myLiveData.observe(this){
Log.i("LiveTag1", it)
}
//连续发送值的改变
myLiveData.postValue("AAAAAAAAAAAAAAAAAA")
myLiveData.postValue("BBBBBBBBBB")
myHandler.postDelayed(Runnable {
//延迟1ms发送
myLiveData.postValue("CCCCCCCCCC")
},1)
}
}
结果
并没有AAA的值发送出去,但是延迟1ms的CCC收到,短时间内LiveData保持了最新设置的值传递,这样是优点 哈哈
LiveData 在短时间内发生多次改变,在值未被分发完之前,观察者只会收到最新的数据变化
接下来看下LiveData值的分发过程
#LiveData类
static final Object NOT_SET = new Object();
private volatile Object mData = NOT_SET;
volatile Object mPendingData = NOT_SET;
protected void postValue(T value) {
boolean postTask;
<1>
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
<2>
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
- 这部分加锁代码保证了mPendingData是最新的值
- 保证在mPostValueRunnable未运行完不会重复运行(其实该线程中就是值分发的过程)
#LiveData类
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//newValue就是最新设置的值
setValue((T) newValue);
}
};
@MainThread
protected void setValue(T value) {
mData = value;
dispatchingValue(null);
}
//使用While分发值
void dispatchingValue(@Nullable ObserverWrapper initiator) {
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
//核心方法
considerNotify(initiator);
initiator = null;
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
//该方法决定分发给那些Observer
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
observer.mObserver.onChanged((T) mData);
}
个人的理解基础上画了个图
代码中知道 实际分发值的时候就是最新的值,原来的值已经被覆盖了
LiveData粘性事件
在使用 LiveData 分发数据的时候,是不会耦合任何 Context 对象的,所以这个机制就从框架层避免了 NPE,OOM 等问题。但于此同时我们也发现了一些问题,比如 LiveData 无法取消黏性事件。如果之前发送的事件,而后注册的观察者也能接收到这条消息,那么是有可能给我们带来麻烦的。
先看个现象
private fun testStick(){
myLiveData.observe(this){
Log.i("LiveTag1", it)
}
myLiveData.postValue("AAAAAAAAAAAAAAAAAA")
myLiveData.observe(this){
Log.i("LiveTag2", it)
}
myHandler.postDelayed(Runnable {
myLiveData.postValue("CCCCCCCCCC")
},1)
}
myLiveData 的第二个观察者竟然也收到了 AAA的改动
原因的话 我们在上一个图中,可以看到其内部通过比对版本号,进而实现了后注册的Observer也能收到之前发送过的事件
如果多个界面共用一个ViewModel(其生命周期大于绑定的所有界面),并且复用了同一个LiveData数据源,LiveData数据源在前一个界面被更新过,那么下一个界面在刚刚绑定观察该LiveData时,就会被通知数据发生变化,LiveData最新值会立即推送过来,在一部分场景下可能显得多此一举,或者引发错误。
参考链接<神羅天徵丶>,这里也提出了解决方案,具体代码去看即可.
此外还有github上有关于LiveDate替换EventBus,RxBus的开源库
ElegantBus(个人推荐 ☆☆☆☆☆)
慕课网的专栏中也提出了一种方案
object HiDataBus {
private val eventMap = ConcurrentHashMap<String, StickyLiveData<*>>()
fun <T> with(eventName: String): StickyLiveData<T> {
//基于事件名称 订阅、分发消息,
//由于 一个 livedata 只能发送 一种数据类型
//所以 不同的event事件,需要使用不同的livedata实例 去分发
var liveData = eventMap[eventName]
if (liveData == null) {
liveData = StickyLiveData<T>(eventName)
eventMap[eventName] = liveData
}
return liveData as StickyLiveData<T>
}
//通过一堆的反射,获取livedata当中的mversion字段,来控制黏性数据的分发与否,但是我们认为这种反射不够优雅。
class StickyLiveData<T>(private val eventName: String) : LiveData<T>() {
internal var mStickyData: T? = null
internal var mVersion = 0
fun setStickyData(stickyData: T) {
mStickyData = stickyData
setValue(stickyData)
//就是在主线程去发送数据
}
fun postStickyData(stickyData: T) {
mStickyData = stickyData
postValue(stickyData)
//不受线程的限制
}
override fun setValue(value: T) {
mVersion++
super.setValue(value)
}
override fun postValue(value: T) {
mVersion++
super.postValue(value)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
observerSticky(owner, false, observer)
}
fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
//允许指定注册的观察则 是否需要关心黏性事件
//sticky =true, 如果之前存在已经发送的数据,那么这个observer会受到之前的黏性事件消息
owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
//监听 宿主 发生销毁事件,主动把livedata 移除掉。
if (event == Lifecycle.Event.ON_DESTROY) {
eventMap.remove(eventName)
}
})
super.observe(owner, StickyObserver(this, sticky, observer))
}
}
class StickyObserver<T>(
val stickyLiveData: StickyLiveData<T>,
val sticky: Boolean,
val observer: Observer<in T>
) : Observer<T> {
//lastVersion 和livedata的version 对齐的原因,就是为控制黏性事件的分发。
//sticky 不等于true , 只能接收到注册之后发送的消息,如果要接收黏性事件,则sticky需要传递为true
private var lastVersion = stickyLiveData.mVersion
override fun onChanged(t: T) {
if (lastVersion >= stickyLiveData.mVersion) {
//就说明stickyLiveData 没有更新的数据需要发送。
if (sticky && stickyLiveData.mStickyData != null) {
observer.onChanged(stickyLiveData.mStickyData)
}
return
}
lastVersion = stickyLiveData.mVersion
observer.onChanged(t)
}
}
}