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 子句在关闭通道时执行特定操作

目录
相关文章
|
2月前
|
Java 编译器 测试技术
Kotlin31 协程如何与 Java 进行混编?
Kotlin31 协程如何与 Java 进行混编?
37 2
Kotlin31 协程如何与 Java 进行混编?
|
3月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
49 1
|
5月前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
441 0
|
7月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
151 2
|
8月前
|
移动开发 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美融合
【5月更文挑战第25天】 在移动开发的世界中,性能和响应性是衡量应用质量的关键指标。随着Kotlin的流行和协程的引入,Android开发者现在有了更强大的工具来提升应用的性能和用户体验。本文深入探讨了Kotlin语言如何与协程相结合,为Android应用开发带来异步处理能力的同时,保持代码的简洁性和可读性。我们将通过实际案例分析,展示如何在Android项目中实现协程,以及它们如何帮助开发者更有效地管理后台任务和用户界面的流畅交互。
|
8月前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin的协程优势
【5月更文挑战第22天】随着移动开发技术的不断进步,Android平台的性能优化已经成为开发者关注的焦点。在众多提升应用性能的手段中,Kotlin语言提供的协程概念因其轻量级线程管理和异步编程能力而受到广泛关注。本文将深入探讨Kotlin协程在Android开发中的应用,以及它如何帮助开发者构建出更高效、响应更快的应用,同时保持代码的简洁性和可读性。
|
8月前
|
移动开发 数据处理 Android开发
构建高效Android应用:Kotlin的协程与Flow的使用
【5月更文挑战第23天】 在移动开发领域,性能优化和异步编程一直是核心议题。随着Kotlin语言在Android开发中的普及,其提供的协程(coroutines)和流式编程(Flow)功能为开发者带来了革命性的工具,以更简洁、高效的方式处理异步任务和数据流。本文将深入探讨Kotlin协程和Flow在Android应用中的实际应用,以及它们如何帮助开发者编写更加响应迅速且不阻塞用户界面的应用程序。我们将通过具体案例分析这两种技术的优势,并展示如何在现有项目中实现这些功能。
|
Java
Kotlin从入门到放弃(三)——协程
引言 这篇主要是将以下kotlin里面的协程,当然这个概念已经随着kotlin的文档被广泛得知了,不过还是用大量代码记录一下吧 一、概念    Coroutine,翻译为协程,意思为各个子任务程协作运行。
2276 0
|
4月前
|
Android开发 开发者 Kotlin
告别AsyncTask:一招教你用Kotlin协程重构Android应用,流畅度飙升的秘密武器
【9月更文挑战第13天】随着Android应用复杂度的增加,有效管理异步任务成为关键。Kotlin协程提供了一种优雅的并发操作处理方式,使异步编程更简单直观。本文通过具体示例介绍如何使用Kotlin协程优化Android应用性能,包括网络数据加载和UI更新。首先需在`build.gradle`中添加coroutines依赖。接着,通过定义挂起函数执行网络请求,并在`ViewModel`中使用`viewModelScope`启动协程,结合`Dispatchers.Main`更新UI,避免内存泄漏。使用协程不仅简化代码,还提升了程序健壮性。
145 1