Kotlin协程开发之Flow的融合与Channel容量及溢出策略介绍

目录
  • 一.协程间的通信
    • 1.通道容量
    • 2.溢出策略
  • 二.FusibleFlow接口
  • 三.ChannelFlow类

一.协程间的通信

当需要进行协程间的通信时,可以调用Channel方法,创建一个Channel接口指向的对象,通过调用该对象的send方法和receive方法实现消息的发送与接收。协程对Channel接口的实现,本质上与阻塞队列类似,这里不再赘述。

1.通道容量

事实上,send方法与receive方法并没有定义在Channel接口中,而是分别定义在SendChannel接口和ReceiveChannel接口中。Channel接口中只是定义了一些与Channel容量策略相关的枚举常量,代码如下:

// 继承SendChannel接口和ReceiveChannel接口
public interface Channel<E> : SendChannel<E>, ReceiveChannel<E> {
    // 枚举常量
    public companion object Factory {
        // Channel的容量为无限
        public const val UNLIMITED: Int = Int.MAX_VALUE
        // Channel的容量为0,没有缓存
        public const val RENDEZVOUS: Int = 0
        // Channel的容量为1,溢出策略为DROP_OLDEST,
        // 后一个的数据会覆盖前一个数据
        public const val CONFLATED: Int = -1
        // Channel的容量为默认值CHANNEL_DEFAULT_CAPACITY,
        // 默认溢出策略为SUSPEND,send方法会发生挂起
        // 当容量策略为BUFFERED,而溢出策略不为SUSPEND时,Channel的容量为1
        public const val BUFFERED: Int = -2
        // 协程内部使用的一个默认枚举值,不对外暴露
        internal const val OPTIONAL_CHANNEL = -3
        // 用于手动配置容量策略为BUFFERED时的默认值
        public const val DEFAULT_BUFFER_PROPERTY_NAME: String = "kotlinx.coroutines.channels.defaultBuffer"
        // 容量策略为BUFFERED时的默认值
        // 默认64,最小1,最大为Int.MAX_VALUE-1
        internal val CHANNEL_DEFAULT_CAPACITY = systemProp(DEFAULT_BUFFER_PROPERTY_NAME,
            64, 1, UNLIMITED - 1
        )
    }
}

从上面的代码可以看出Channel接口继承自SendChannel接口和ReceiveChannel接口。因此,一个Channel接口指向的对象,既可以用于发送消息,也可以用于接收消息。

2.溢出策略

Channel除了容量策略外,还有溢出策略,用于决定当Channel的容量已满时,而下一个消息到来时的行为。溢出策略定义在枚举类BufferOverflow中,代码如下:

public enum class BufferOverflow {
    // 当容量已满时,挂起调用send方法的协程
    SUSPEND,
    // 当容量已满时,删除旧数据,将新的数据添加进去,不挂起调用send方法的协程
    DROP_OLDEST,
    // 当容量已满时,忽略当前要添加的数据,不挂起调用send方法的协程
    DROP_LATEST
}

二.FusibleFlow接口

FusibleFlow接口继承自Flow接口。一个类实现了该接口,表示该类创建的流可以与其上游或下游相邻的流进行融合,当流发生融合时,就会调用接口中定义的fuse方法,代码如下:

@InternalCoroutinesApi
public interface FusibleFlow<T> : Flow<T> {
    // 用于流的融合
    public fun fuse(
        context: CoroutineContext = EmptyCoroutineContext,
        capacity: Int = Channel.OPTIONAL_CHANNEL,
        onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND
    ): Flow<T>
}

FusibleFlow接口的fuse方法,默认容量为OPTIONAL_CHANNEL,默认溢出策略为SUSPEND。

流的融合

在Flow中,当channelFlow方法、flowOn方法、buffer方法、produceIn方法、broadcastIn方法相邻调用时,就会触发流的融合。

具体融合的过程,其实是将下游流的容量、溢出策略、上下文传递给上游的流处理,上游的流根据自身的容量、溢出策略、上下文以及下游的流的容量、溢出策略、上下文重新计算,得到新的容量、溢出策略、上下文,并返回一个融合后的流。

三.ChannelFlow类

ChannelFlow类是一个抽象类,实现了FusibleFlow接口。下面分析一下fuse方法对于上下游流融合的策略,代码如下:

@InternalCoroutinesApi
public abstract class ChannelFlow<T>(
    // 上游流的上下文
    @JvmField public val context: CoroutineContext,
    // 上下游之间流的缓存容量
    @JvmField public val capacity: Int,
    // 溢出策略
    @JvmField public val onBufferOverflow: BufferOverflow
) : FusibleFlow<T> {
    ...
    public override fun fuse(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow): Flow<T> {
        // CONFLATED是一个复合的类型,需要拆解成capacity = 0, onBufferOverflow = DROP_OLDEST
        assert { capacity != Channel.CONFLATED }
        // 计算融合后流的上下文
        val newContext = context + this.context
        // 用于保存融合后流的容量
        val newCapacity: Int
        // 用于保存融合后流的溢出策略
        val newOverflow: BufferOverflow
        // SUSPEND为默认溢出策略,如果溢出策略不是默认的策略
        if (onBufferOverflow != BufferOverflow.SUSPEND) {
            // 直接保存
            newCapacity = capacity
            newOverflow = onBufferOverflow
        } else { // 如果是默认策略
            // 计算并保存新的容量
            newCapacity = when {
                // 如果之前的容量为默认枚举值,则使用新的
                this.capacity == Channel.OPTIONAL_CHANNEL -> capacity
                // 如果新的容量为默认枚举值,则使用原来的
                capacity == Channel.OPTIONAL_CHANNEL -> this.capacity
                // 如果原来的容量为默认值CHANNEL_DEFAULT_CAPACITY,则使用新的
                this.capacity == Channel.BUFFERED -> capacity
                // 如果新的容量为默认值CHANNEL_DEFAULT_CAPACITY,则使用原来的
                capacity == Channel.BUFFERED -> this.capacity
                // 如果不为默认值或默认枚举值
                else -> {
                    // 检查容量都是大于等于0的
                    assert { this.capacity >= 0 }
                    assert { capacity >= 0 }
                    // 将原来的容量和新的容量进行相加
                    val sum = this.capacity + capacity
                    // 如果相加后大与等于0,则容量为相加后的结果,否则为无限
                    if (sum >= 0) sum else Channel.UNLIMITED
                }
            }
            // 保存溢出策略
            newOverflow = this.onBufferOverflow
        }
        // 如果融合的两个流的上下文相同,容量相同,溢出策略也相同
        if (newContext == this.context && newCapacity == this.capacity && newOverflow == this.onBufferOverflow)
            // 则直接返回
            return this
        // 有变化则根据新计算出得参数,创建融合后的流
        return create(newContext, newCapacity, newOverflow)
    }
    // 由子类进行重写
    protected abstract fun create(context: CoroutineContext, capacity: Int, onBufferOverflow: BufferOverflow): ChannelFlow<T>
    ...
}

流融合的原则

根据上面对fuse方法的分析,可以总结出fuse方法在计算容量和溢出策略时的四个原则:

1)下游优先于上游

2)溢出策略优先于容量

3)非默认值优先于默认值

4)上下游容量都不为默认值,则相加取和

到此这篇关于Kotlin协程开发之Flow的融合与Channel容量及溢出策略介绍的文章就介绍到这了,更多相关Kotlin Flow的融合内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Kotlin协程操作之创建启动挂起恢复详解

    目录 一.协程的创建 1.start方法 2.CoroutineStart类 3.startCoroutineCancellable方法 4.createCoroutineUnintercepted方法 5.createCoroutineFromSuspendFunction方法 二.协程的启动 1.ContinuationImpl类 2.resumeCancellableWith方法 3.BaseContinuationImpl类 4.invokeSuspend方法 三.协程的挂起与恢复 下面

  • Kotlin协程启动createCoroutine及创建startCoroutine原理

    目录 createCoroutine 和 startCoroutine startCoroutine调用 createCoroutineUnintercepted intercepted resume 结语 createCoroutine 和 startCoroutine 协程到底是怎么创建和启动的?本篇文章带你揭晓. 在Continuation.kt文件中,有2个基础API,这里单独提出来说一下,方便后面我们理解launch. public fun <T> (suspend () ->

  • kotlin 协程上下文异常处理详解

    目录 引言 一.协程上下文 1.CoroutineContext 2.CorountineScope 3.子协程继承父协程 二.协程的异常传递 1.协程的异常传播 2.不同上下文(没有继承关系)之间协程异常会怎么样? 3.向用户暴露异常 三.协程的异常处理 使用SupervisorJob 异常捕获器CoroutineExceptionHandler Android中全局异常的处理 引言 从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • Kotlin协程概念原理与使用万字梳理

    目录 一.协程概述 1.概念 2.特点 3.原理 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 三.协程使用 1.协程的启动 2.协程间通信 3.多路复用 4.序列生成器 5.协程异步流 6.全局上下文 一.协程概述 1.概念 协程是Coroutine的中文简称,co表示协同.协作,routine表示程序.协程可以理解为多个互相协作的程序.协程是轻量级的线程,它的轻量体现在启动和切换,协程的启动不需要申请额外的堆栈空间:协程的切换发生在

  • Kotlin协程launch原理详解

    目录 正文 launch使用 launch原理 CoroutineStart中找invoke方法 startCoroutineCancellable逻辑 小结 正文 launch我们经常用,今天来看看它是什么原理. 建议: 食用本篇文章之前记得先食用Kotlin协程之createCoroutine和startCoroutine launch使用 launch我们应该很熟悉了,随便举个例子: fun main() { val coroutineScope = CoroutineScope(Job(

  • Kotlin协程的启动方式介绍

    目录 1.GlobalScope.launch 2.runBlocking 启动协程 3.async启动协程 启动协程的基本方式 1.GlobalScope.launch 代码示例: fun testGlobalScope() { GlobalScope.launch { println("Coroutinue started!") delay(1000L) println("Hello World!") } println("After launch!&

  • Kotlin协程flowOn与线程切换超详细示例介绍

    目录 示例代码 一.flowOn方法 1.ChannelFlowOperatorImpl类 二.collect方法 1.ChannelFlowOperator类的collect方法 2.ChannelFlow类的collect方法 3.flow方法中代码的执行 4.接收flow方法发出的值 三.flowOn方法与流的融合 四.总结 示例代码 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.flowOn(Dispatc

  • Kotlin协程Dispatchers原理示例详解

    目录 前置知识 demo startCoroutineCancellable intercepted()函数 DefaultScheduler中找dispatch函数 Runnable传入 Worker线程执行逻辑 小结 前置知识 Kotlin协程不是什么空中阁楼,Kotlin源代码会被编译成class字节码文件,最终会运行到虚拟机中.所以从本质上讲,Kotlin和Java是类似的,都是可以编译产生class的语言,但最终还是会受到虚拟机的限制,它们的代码最终会在虚拟机上的某个线程上被执行. 之

  • Kotlin协程开发之Flow的融合与Channel容量及溢出策略介绍

    目录 一.协程间的通信 1.通道容量 2.溢出策略 二.FusibleFlow接口 三.ChannelFlow类 一.协程间的通信 当需要进行协程间的通信时,可以调用Channel方法,创建一个Channel接口指向的对象,通过调用该对象的send方法和receive方法实现消息的发送与接收.协程对Channel接口的实现,本质上与阻塞队列类似,这里不再赘述. 1.通道容量 事实上,send方法与receive方法并没有定义在Channel接口中,而是分别定义在SendChannel接口和Rec

  • Kotlin协程Flow生命周期及异常处理浅析

    目录 正文 Flow基本概念 Flow生命周期 处理异常 上游或者中间异常使用catch 下游使用try-catch 切换执行线程 终止操作符 "冷的数据流"从何而来 正文 Kotlin协程中的Flow主要用于处理复杂的异步数据,以一种”流“的方式,从上到下依次处理,和RxJava的处理方式类型,但是比后者更加强大. Flow基本概念 Flow中基本上有三个概念,即 发送方,处理中间层,接收方,可以类比水利发电站中的上游,发电站,下游的概念, 数据从上游开始发送”流淌“至中间站被”处理

  • Android kotlin+协程+Room数据库的简单使用

    Room Room是Google为了简化旧版的SQLite操作专门提供的 1.拥有了SQLite的所有操作功能 2.使用简单(类似于Retrofit),通过注解的方式实现相关功能.编译时自动生成实现类impl 3.LiveData,LifeCycle,Paging天然融合支持 导入 ... plugins { id 'com.android.application' id 'kotlin-android' id 'kotlin-android-extensions' id 'kotlin-kap

  • Kotlin协程的基础与使用示例详解

    目录 一.协程概述 1.概念 2.特点 3.原理 1)续体传递 2)状态机 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 1)协程状态的转换 2)状态标识的变化 三.协程使用 1.协程的启动 1)runBlocking方法 2)launch方法 3)async方法 4)suspend关键字 5)withContext方法 6)suspend方法 2.协程间通信 1)Channel 2)Channel的容量 3)produce方法与act

  • Kotlin 协程思维模型的引入使用建立

    目录 1.协程 2.Kotlin协程 1.引入Kotlin协程 2.Kotlin协程的使用 3.Kotlin协程的轻量(总结的还不够清晰) 4.协程的“非阻塞式” 5.建立思维模型 1.协程 协程不是进程或线程,它的执行过程更类似于子例程或者说不带返回值的函数调用. 一个程序可以包含多个协程,类似于一个进程包含多个线程.线程有自己的上下文多个线程存在时它们相对独立,切换受系统控制,而协程也相对独立,也有自己的上下文,但是切换是由自己控制的,当需要切换到其他协程时是由当前协程控制的. 线程 协程

  • 一篇文章揭开Kotlin协程的神秘面纱

    前言 Kotlin协程提供了一种新的异步执行方式,但直接查看库函数可能会有点混乱,本文中尝试揭开协程的神秘面纱. 理论 它是什么 这是别人翻译: 协程把异步编程放入库中来简化这类操作.程序逻辑在协程中顺序表述,而底层的库会将其转换为异步操作.库会将相关的用户代码打包成回调,订阅相关事件,调度其执行到不同的线程(甚至不同的机器),而代码依然想顺序执行那么简单. 我的理解:子任务程协作运行,优雅的处理异步问题解决方案. 它能干什么? 我在做安卓开发,它能替换掉Handler,AsyncTask 甚至

  • 使用kotlin协程提高app性能(译)

    协程是一种并发设计模式,您可以在Android上使用它来简化异步执行的代码.Kotlin1.3版本添加了 Coroutines,并基于其他语言的既定概念. 在Android上,协程有助于解决两个主要问题: 管理长时间运行的任务,否则可能会阻止主线程并导致应用冻结. 提供主安全性,或从主线程安全地调用网络或磁盘操作. 本主题描述了如何使用Kotlin协程解决这些问题,使您能够编写更清晰,更简洁的应用程序代码. 管理长时间运行的任务 在Android上,每个应用程序都有一个主线程来处理用户界面并管理

随机推荐