Kotlin | 协程使用手册(不间断更新)(下)

简介: Kotlin协程作为Kotlin核心的一个组件,上手成本并不高,下面的demo都是我参照官网的例子过了一遍。

合并

conflate

用于跳过中间的值,只处理最新的值。

fun main() {
    measureTimeMillis {
        runBlocking {
            (1..5).asFlow()
                .conflate()
                .buffer()
                .collect {
                    delay(100)
                    println(it)
                }
        }
    }.let{
    println("花费时间-${it}ms")
    }
}
1
5
花费时间-325ms

处理最新值

collectLatest & conf

取消缓慢的收集器,并在每次发射新值的时候重新启动它。

fun main() {
    measureTimeMillis {
        runBlocking {
            (1..5).asFlow()
                .collectLatest {
                    println(it)
                    delay(100)
                    println("重新发射-$it")
                }
        }
    }.let {
        println("花费时间-${it}ms")
    }
}
1
2
3
4
5
重新发射-5
花费时间-267ms

组合流

zip

组合两个流中的相关值

fun main() {
    measureTimeMillis {
        val strs= flowOf("one","two","three")
        runBlocking {
            (1..5).asFlow()
                .zip(strs){
                    a,b ->
                    "$a -> $b"
                }.collect { println(it) }
        }
    }.let {
        println("花费时间-${it}ms")
    }
}
1 -> one
2 -> two
3 -> three
花费时间-120ms

Combine

suspend fun main() {
    val nums = (1..3).asFlow().onEach { delay(300) } // 发射数字 1..3,间隔 300 毫秒
    val strs = flowOf("one", "two", "three").onEach { delay(400) } // 每 400 毫秒发射一次字符串
    val startTime = System.currentTimeMillis() // 记录开始的时间
    nums.combine(strs) { a, b -> "$a -> $b" } // 使用“zip”组合单个字符串
        .collect { value -> // 收集并打印
            println("$value at ${System.currentTimeMillis() - startTime} ms from start")
        }
}
1 -> one at 472 ms from start
2 -> one at 682 ms from start
2 -> two at 874 ms from start
3 -> two at 987 ms from start
3 -> three at 1280 ms from start

通道Channel

  • 非阻塞的通信基础设施
  • 类似于 BlockingQueue+suspend

提供了一种在 Flow 中传递数据的方法

Channel的分类

image.png

Channel基础

suspend fun main() {
   runBlocking {
       val channel = Channel<Int>()
       launch {
           // 这里可能是消耗大量 CPU 运算的异步逻辑,我们将仅仅做 5 次整数的平方并发送
           for (x in 1..5) channel.send(x * x)
       }
// 这里我们打印了 5 次被接收的整数:
       repeat(5) { println(channel.receive()) }
       println("Done!")
   }
}
1
4
9
16
Done!

channel的关闭与迭代

suspend fun main() {
    runBlocking {
        val channel = Channel<Int>()
        launch {
            // 这里可能是消耗大量 CPU 运算的异步逻辑,我们将仅仅做 5 次整数的平方并发送
            for (x in 1..5) {
                channel.send(x * x)
            }
            channel.close()
            println("通道是否关闭+${channel.isClosedForSend}")
        }
        for (i in channel) {
            println(i)
        }
        println("是否接收了所有的数据+${channel.isClosedForReceive}")
    }
}
1
4
9
16
25

consumeEach

用以代替for循环

suspend fun main() {
   runBlocking {
       val channel = Channel<Int>()
       launch {
           // 这里可能是消耗大量 CPU 运算的异步逻辑,我们将仅仅做 5 次整数的平方并发送
           for (x in 1..5) {
               channel.send(x * x)
           }
           channel.close()
       }
       channel.consumeEach(::println)
   }
}

Channel的协程Builder

  • Produce 启动一个生产者协程,返回ReceiveChannel
  • actor : 启动一个消费者协程,返回 SendChannel (暂时废弃)
  • 以上Builder启动的协程结束后悔自动关闭对应的 Channel

Produce

suspend fun main() {
    val receiveChannel = GlobalScope.produce(capacity = Channel.UNLIMITED) {
        for (i in 0..3) {
            send(i)
        }
    }
      GlobalScope.launch {
        receiveChannel.consumeEach(::println)
    }.join()
}
0
1
2
3

actor

suspend fun main() {
    val sendChannel = GlobalScope.actor<Int>(capacity = Channel.UNLIMITED) {
        channel.consumeEach(::println)
    }
    GlobalScope.launch {
        for (i in 1..3){
            sendChannel.send(i)
        }
    }.join()
}

BroadcastChannel

  • Channel 的元素只能被一个消费者消费
  • BroadcastChannel 的元素可以分发给所有的订阅者
  • BroadcastChannel 不支持RENDEZVOUS
suspend fun main() {
    val broadcastChannel = GlobalScope.broadcast {
        for (i in 1..3)
            send(i)
    }
    List(3) {
        GlobalScope.launch {
            val receiveChannel = broadcastChannel.openSubscription()
            println("-----$it")
            receiveChannel.consumeEach(::println)
        }
    }.joinAll()
}
-----2
-----0
-----1
1
1
1
2
2
3
3
2
3

Select(实验性)

  • 是一个IO多路复用的概念
  • koltin中用于挂起函数的多路复用

Select表达式可以同时等待多个挂起函数,并选择第一个可用的

在Channel使用

suspend fun main() {
    runBlocking {
        val fizz = fizz()
        val buzz = buzz()
        repeat(7) {
            selectFizzBuzz(fizz, buzz)
        }
        coroutineContext.cancelChildren()
    }
}
suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
    select<Unit> {
        // <Unit> 意味着该 select 表达式不返回任何结果
        fizz.onReceive { value ->
            // 这是第一个 select 子句
            println("fizz -> '$value'")
        }
        buzz.onReceive { value ->
            // 这是第二个 select 子句
            println("buzz -> '$value'")
        }
    }
}
@ExperimentalCoroutinesApi
fun CoroutineScope.fizz() = produce {
    while (true) {
        delay(300)
        send("Fizz")
    }
}
@ExperimentalCoroutinesApi
fun CoroutineScope.buzz() = produce {
    while (true) {
        delay(500)
        send("Buzz")
    }
}
fizz -> 'Fizz'
buzz -> 'Buzz'
fizz -> 'Fizz'
fizz -> 'Fizz'
buzz -> 'Buzz'
fizz -> 'Fizz'
buzz -> 'Buzz'

使用receive 挂起函数,我们可以从两个通道中接收其中一个数据,但是select允许我们使用其 onReceive 子句同时从两者接收。

注意:onReceiver 在已经该关闭的通道执行会发生失败并抛出异常,我们可以使用onReceiveOrNull 子句在关闭通道时执行特定操作

目录
相关文章
|
30天前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
19 1
|
3月前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
205 0
|
5月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
124 2
|
6月前
|
移动开发 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美融合
【5月更文挑战第25天】 在移动开发的世界中,性能和响应性是衡量应用质量的关键指标。随着Kotlin的流行和协程的引入,Android开发者现在有了更强大的工具来提升应用的性能和用户体验。本文深入探讨了Kotlin语言如何与协程相结合,为Android应用开发带来异步处理能力的同时,保持代码的简洁性和可读性。我们将通过实际案例分析,展示如何在Android项目中实现协程,以及它们如何帮助开发者更有效地管理后台任务和用户界面的流畅交互。
|
6月前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin的协程优势
【5月更文挑战第22天】随着移动开发技术的不断进步,Android平台的性能优化已经成为开发者关注的焦点。在众多提升应用性能的手段中,Kotlin语言提供的协程概念因其轻量级线程管理和异步编程能力而受到广泛关注。本文将深入探讨Kotlin协程在Android开发中的应用,以及它如何帮助开发者构建出更高效、响应更快的应用,同时保持代码的简洁性和可读性。
|
6月前
|
移动开发 数据处理 Android开发
构建高效Android应用:Kotlin的协程与Flow的使用
【5月更文挑战第23天】 在移动开发领域,性能优化和异步编程一直是核心议题。随着Kotlin语言在Android开发中的普及,其提供的协程(coroutines)和流式编程(Flow)功能为开发者带来了革命性的工具,以更简洁、高效的方式处理异步任务和数据流。本文将深入探讨Kotlin协程和Flow在Android应用中的实际应用,以及它们如何帮助开发者编写更加响应迅速且不阻塞用户界面的应用程序。我们将通过具体案例分析这两种技术的优势,并展示如何在现有项目中实现这些功能。
|
5月前
|
Go Python
使用python实现一个用户态协程
【6月更文挑战第28天】本文探讨了如何在Python中实现类似Golang中协程(goroutines)和通道(channels)的概念。文章最后提到了`wait_for`函数在处理超时和取消操作中的作
51 1
使用python实现一个用户态协程
|
2月前
|
调度 Python
python3 协程实战(python3经典编程案例)
该文章通过多个实战案例介绍了如何在Python3中使用协程来提高I/O密集型应用的性能,利用asyncio库以及async/await语法来编写高效的异步代码。
20 0
|
4月前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
【7月更文挑战第15天】Python的协程与异步函数优化Web性能,通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器,示例代码展示如何处理GET请求。异步处理减少资源消耗,提高响应速度和吞吐量,适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
83 10
|
4月前
|
数据处理 Python
深入探索:Python中的并发编程新纪元——协程与异步函数解析
【7月更文挑战第15天】Python 3.5+引入的协程和异步函数革新了并发编程。协程,轻量级线程,由程序控制切换,降低开销。异步函数是协程的高级形式,允许等待异步操作。通过`asyncio`库,如示例所示,能并发执行任务,提高I/O密集型任务效率,实现并发而非并行,优化CPU利用率。理解和掌握这些工具对于构建高效网络应用至关重要。
48 6