携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
上篇讲解了通过
liveData{}创建CoroutineLiveData的基本使用,本篇文章主要介绍CoroutineLiveData的原理分析,这是一个基于协程+MediatorLiveData实现的一种build构建livedata的类。
历史文章
肢解LiveData:协程味的CoroutineLiveData了解一下(一)
liveData{}入口分析
public fun <T> liveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
@BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)
实际上创建了一个CoroutineLiveData对象,我们看下这个对象:
CoroutineLiveData
internal class CoroutineLiveData<T>(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
block: Block<T>
) : MediatorLiveData<T>()
这个类继承了MediatorLiveData,这就为emitSource()的实现埋下了伏笔,稍后分析。MediatorLiveData有两个很关键的属性:
-
blockRunner: 该类真正执行liveData{}中的代码块,实现给LiveData赋值; -
emittedSource:专门用于移除emitSource()方法添加的LiveData数据源监听;
接下来我们从MediatorLiveData的方法onActive()作为入口进行分析。
onActive()
override fun onActive() {
super.onActive()
blockRunner?.maybeRun()
}
这个方法大家很熟悉,就是当界面状态大于Started时就会被回调执行,该方法会调用到blockRunner的maybeRun()方法:
BlockRunner.maybeRun()
@MainThread
fun maybeRun() {
//省略
runningJob = scope.launch {
val liveDataScope = LiveDataScopeImpl(liveData, coroutineContext)
block(liveDataScope)
onDone()
}
}
请注意
scope协程作用域指定的调度器为Dispatchers.Main.immediate,所以说liveData{}中的代码块会默认在主线程中执行。
- 首先创建一个
LiveDataScopeImpl对象,我们在liveData{}中调用emit()和emitSource()方法都是来自于它:
-
调用
block(liveDataScope),这个block就是liveData{}中代码块; -
当代码块执行完毕就会执行传入
onDone(),将CoroutineLiveData的blockRunner置null,看可以说是非常的严禁了。
接下来我们来一一看下emit()和emitSource()的实现。
LiveDataScopeImpl.emit()
override suspend fun emit(value: T) = withContext(coroutineContext) {
target.clearSource()
target.value = value
}
这个target对象就是我们创建的CoroutineLiveData:
-
调用
clearSource()取消之前通过emitSource()添加的LiveData数据源监听,这也就是之前说的调用emit()方法会让emitSource()添加的数据源监听失效; -
调用
CoroutineLiveData的方法setValue()进行赋值,也和我们自己平常创建LiveData并手动赋值一样,这样LiveData添加的监听者Observer就可以收到回调了。(界面状态大于Started)
LiveDataScopeImpl.emitSource()
override suspend fun emitSource(source: LiveData<T>): DisposableHandle =
withContext(coroutineContext) {
return@withContext target.emitSource(source)
}
最终调用CoroutineLiveData.的emitSource()方法。
CoroutineLiveData.emitSource()
internal suspend fun emitSource(source: LiveData<T>): DisposableHandle {
clearSource()
val newSource = addDisposableSource(source)
emittedSource = newSource
return newSource
}
-
清楚之前添加的其他LiveData数据源监听,也就是说通过
emitSource()只能添加一个LiveData监听; -
调用
addDisposableSource()方法
internal suspend fun <T> MediatorLiveData<T>.addDisposableSource(
source: LiveData<T>
//1.
): EmittedSource = withContext(Dispatchers.Main.immediate) {
//2.
addSource(source) {
value = it
}
//3.
EmittedSource(
source = source,
mediator = this@addDisposableSource
)
}
-
指定协程代码块在主线程中执行;
-
调用
MediatorLiveData的addSource()方法添加数据源监听:这个方法大家应该很熟悉了,这个
source参数就是通过emitSource()方法的参数传入的。我们简单看下addSource()方法的实现:关键在于
Source类的创建:即使给我们监听的
LiveData添加一个Obserer监听,并赋值给上面的CoroutineLiveData。 -
构造一个
EmittedSource管理从MediatorLiveData移除上面通过addSource()添加的LiveData数据源监听。
总结
本篇文章分析了liveData{}的实现原理,核心在emit()和emitSource()的实现,以及使用过程中的注意点。