Coroutine之Flow与LiveData相互转换

1. Flow与LiveData相互转换

1.1. Flow转换成LiveData

Flow提供了asLiveData()扩展函数来将Flow转换成LiveData:

//CoroutineLiveData.kt
@JvmOverloads
fun <T> Flow<T>.asLiveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
    collect {
        emit(it)
    }
}

fun <T> liveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT,
    @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)

asLiveData()方法主要就是创建一个CoroutineLiveData对象,CoroutineLiveData继承于MediatorLiveData,先看一下这个类的初始化方法:

//class CoroutineLiveData
init {
        // use an intermediate supervisor job so that if we cancel individual block runs due to losing
        // observers, it won't cancel the given context as we only cancel w/ the intention of possibly
        // relaunching using the same parent context.
        val supervisorJob = SupervisorJob(context[Job])

        // The scope for this LiveData where we launch every block Job.
        // We default to Main dispatcher but developer can override it.
        // The supervisor job is added last to isolate block runs.
        val scope = CoroutineScope(Dispatchers.Main.immediate + context + supervisorJob)
        blockRunner = BlockRunner(
            liveData = this,
            block = block,
            timeoutInMs = timeoutInMs,
            scope = scope
        ) {
            blockRunner = null
        }
    }

这里构造了一个SupervisorJob对象,根据注释可知这是用于隔离父子协程的,以达到取消子协程而不取消父协程的作用。还构造了一个BlockRunner对象,用于包装LiveData及Flow的collect操作,这里的block即代表Flow的collect操作。根据LiveData原理,在生命周期拥有者的生命周期发上变化时会通知LiveData的生命周期观察者,当生命周期拥有者的生命周期状态变为active状态时,LiveData的onActive()方法会被调用:

//class CoroutineLiveData
override fun onActive() {
    super.onActive()
    blockRunner?.maybeRun()
}

//class BlockRunner
fun maybeRun() {
     cancellationJob?.cancel()
     cancellationJob = null
     if (runningJob != null) {
         return
     }
     runningJob = scope.launch {
         val liveDataScope = LiveDataScopeImpl(liveData, coroutineContext)
         block(liveDataScope)
         onDone()
     }
 }

这里先忽略cancellationJob,先创建一个LiveDataScopeImpl对象,然后以其为参数执行block代码块,执行完后再执行onDone代码块,即blockRunner = null。回到block代码块赋值的地方,会执行Flow的扩展方法collect(),这一过程就可以参考Kotlin中flow发射与接收分析。简要看一下这一步的代码:

public suspend inline fun <T> Flow<T>.collect(crossinline action: suspend (value: T) -> Unit): Unit =
    collect(object : FlowCollector<T> {
        override suspend fun emit(value: T) = action(value)
    })

当原Flow中有数据发射时,action lambda表达式即会执行,这里即会执行LiveDataScopeImpl对象中的emit()方法:

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

这里的target即使本LiveData对象,执行其setValue()方法就会触发LiveData中的观察者被通知。从上面的分析可知,在生命周期拥有者处于active状态时,会触发对原Flow的collect操作,下面看一下当生命周期拥有者处于inactive状态时,又做了什么:

//class CoroutineLiveData
override fun onInactive() {
        super.onInactive()
        blockRunner?.cancel()
    }

//class BlockRunner
@MainThread
fun cancel() {
    if (cancellationJob != null) {
        error("Cancel call cannot happen without a maybeRun")
    }
    cancellationJob = scope.launch(Dispatchers.Main.immediate) {
        delay(timeoutInMs)
        if (!liveData.hasActiveObservers()) {
            // one last check on active observers to avoid any race condition between starting
            // a running coroutine and cancelation
            runningJob?.cancel()
            runningJob = null
        }
    }
}

可以看到开启一个协程,延迟timeoutInMs毫秒,默认是5秒,然后将runningJob协程取消掉。这里延迟5秒主要是为了避免在横竖屏切换时做无谓的工作,当横竖屏切换时会先执行onInactive()方法再执行onActive()方法,档其间隔小于5秒时runningJob?.cancel()还没有执行cancellationJob协程就被取消掉了,因此根本没有取消对原Flow的collect操作。

1.2. LiveData转换成Flow

LiveData提供了扩展函数asFlow()来将LiveData转换成Flow:

fun <T> LiveData<T>.asFlow(): Flow<T> = flow {
    val channel = Channel<T>(Channel.CONFLATED)
    val observer = Observer<T> {
        channel.offer(it)
    }
    withContext(Dispatchers.Main.immediate) {
        observeForever(observer)
    }
    try {
        for (value in channel) {
            emit(value)
        }
    } finally {
        GlobalScope.launch(Dispatchers.Main.immediate) {
            removeObserver(observer)
        }
    }
}

这段代码就相对简单了,构造了一个Channel对象来做为中转,然后再给LiveData对象添加一个Observer观察者,当观察者被通知时即将该通知数据放入Channel中,然后在将该Channel对象中的数据取出构造成flow。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值