肢解LiveData:协程味的CoroutineLiveData了解一下(二)-灵析社区

江江说技术

上篇讲解了通过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时就会被回调执行,该方法会调用到blockRunnermaybeRun()方法:

BlockRunner.maybeRun()

@MainThread
fun maybeRun() {
    //省略
    runningJob = scope.launch {
        val liveDataScope = LiveDataScopeImpl(liveData, coroutineContext)
        block(liveDataScope)
        onDone()
    }
}
请注意scope协程作用域指定的调度器为Dispatchers.Main.immediate,所以说liveData{}中的代码块会默认在主线程中执行。
  1. 首先创建一个LiveDataScopeImpl对象,我们在liveData{}中调用emit()emitSource()方法都是来自于它:

2.调用block(liveDataScope),这个block就是liveData{}中代码块;

3.当代码块执行完毕就会执行传入onDone(),将CoroutineLiveData的blockRunner置null,看可以说是非常的严禁了。

接下来我们来一一看下emit()emitSource()的实现。

LiveDataScopeImpl.emit()

override suspend fun emit(value: T) = withContext(coroutineContext) {
    target.clearSource()
    target.value = value
}

这个target对象就是我们创建的CoroutineLiveData

  1. 调用clearSource()取消之前通过emitSource()添加的LiveData数据源监听,这也就是之前说的调用emit()方法会让emitSource()添加的数据源监听失效;
  2. 调用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
}
  1. 清楚之前添加的其他LiveData数据源监听,也就是说通过emitSource()只能添加一个LiveData监听;
  2. 调用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
    )
}
  1. 指定协程代码块在主线程中执行;

2.调用 MediatorLiveDataaddSource()方法添加数据源监听:

这个方法大家应该很熟悉了,这个source参数就是通过emitSource()方法的参数传入的。我们简单看下addSource()方法的实现:

Source类的创建:

  • 即使给我们监听的LiveData添加一个Obserer监听,并赋值给上面的CoroutineLiveData。
  • 3.构造一个EmittedSource管理从MediatorLiveData移除上面通过addSource()添加的LiveData数据源监听。
  • 总结

    本篇文章分析了liveData{}的实现原理,核心在emit()emitSource()的实现,以及使用过程中的注意点。

    阅读量:270

    点赞量:0

    收藏量:0