我是靠谱客的博主 平淡棒棒糖,最近开发中收集的这篇文章主要介绍Coroutine之Flow与LiveData相互转换1. Flow与LiveData相互转换,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

  • 1. Flow与LiveData相互转换
    • 1.1. Flow转换成LiveData
    • 1.2. LiveData转换成Flow

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。

最后

以上就是平淡棒棒糖为你收集整理的Coroutine之Flow与LiveData相互转换1. Flow与LiveData相互转换的全部内容,希望文章能够帮你解决Coroutine之Flow与LiveData相互转换1. Flow与LiveData相互转换所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(73)

评论列表共有 0 条评论

立即
投稿
返回
顶部