前言
使用Kotlin做Android项目时,肯定少不了使用协程,而在协程的使用中,少不了要在不同的协程中传递数据,而Kotlin中的Channel,就是专门用来处理协程之间的通信,今天这篇就是来看看Channel的用法。
Channel简介
channel用于协程间的通信, 允许我们在不同的协程间传递数据。channel非常类似于一个 java 中非常常见的概念BlockingQueue,元素从一端被加入, 从另一端被消费.。当需要的时候, 多个协程可以向同一个channel发送数据, 一个channel的数据也可以被多个协程接收。当多个协程从同一个channel接收数据的时候, 每个元素仅被其中一个consumer消费一次. 处理元素会自动将其从channel里删除。
Channel的用法
channel可以简单的通过send和receive实现。
代码语言:javascript复制fun main() = runBlocking{
val intchannel = Channel<Int>()
launch {
for(x in 1..10) intchannel.send(x)
}
repeat(10){
val recv = intchannel.receive()
println("read:$recv")
}
}
输出结果
channel还可以通过遍历来实现receive。
代码语言:javascript复制fun main() = runBlocking{
val intchannel = Channel<Int>()
launch {
for(x in 1..6) intchannel.send(x)
}
for(recv in intchannel) println("read:$recv")
println("receive finish")
}
输出结果
从上图中可以看到,通过for的方式可以直接赋值到recv里打印出来了,但是在代码的结尾中我们的println("receive finish"),并没有在控制台打印出来,程序也没有退出,这是因为接收者在协程中还一直在等待。
想要正常结束并退出,接下来就要用到channel的关闭了,Channel可以被关闭, 说明没有更多的元素了。取消协程也会关闭channel。那我们改一下上面的代码,加上close。
代码语言:javascript复制fun main() = runBlocking{
val intchannel = Channel<Int>()
launch {
for(x in 1..6) intchannel.send(x)
intchannel.close()
}
for(recv in intchannel) println("read:$recv")
println("receive finish")
}
代码中加入了close后,可以看到,channel接收完后,打印出的finish并且退出程序了。
channel的类型
Channel有四种不同的类型,类型间不同的区别有两点,一是定义内部可以存储的元素,二是Send方式是否可以被挂起;而所有channel类型的Receive方法都是同样的行为,如果channel不为空, 接收一个元素, 否则挂起,具体的类型如下:
- Rendezvous channel(默认类型): 0尺寸buffer,Send和Receive要见面,否则Send挂起,就是说每Send完的后,如果一直没有Receive,即被挂起。
- Buffered channel:指定元素大小,当满了后Send会被挂起。
- Conflated channel: 新元素会覆盖旧元素,receiver只会得到最新元素,Send永不挂起。
- Unlimited channel: 无限元素,Send不被挂起。
接下来我们做个用多个协程向同一个Channel发送数据的例子来看看这四种模式。
01Rendezvous channel(默认类型)
代码语言:javascript复制fun main() {
val channel = Channel<String>()
runBlocking {
val res1 = async {
for (x in 1..5) {
channel.send("I am $x")
}
return@async "res1 over"
}
val res2 = async {
for (y in 11..15) {
channel.send("you are $y")
}
return@async "res2 over"
}
val res3 = async {
for (z in 21..25) {
channel.send("he is $z")
}
return@async "res3 over"
}
launch {
println(" async finish ${res1.await() res2.await() res3.await()} ")
channel.close()
}
for (item in channel) {
println(item)
}
println("finish")
}
}
代码中用async开启三个协程,当三个协程执行完后,关闭channel。下面是输出结果。
02Buffered channel
代码语言:javascript复制val channel = Channel<String>(3)
当channel中后面加上数字3,然后再运行看。
上图中,因为满了3个后Send挂起,所以第一个协程(1-5)完后,第二个协程的11数字进去后也开始挂起了,这时的挂起也让第三个协程(21-25)的第一条进入到队列中。
03Conflated channel
代码语言:javascript复制val channel = Channel<String>(Channel.CONFLATED)
当channel中后面参数加上Channel.CONFLATED,然后再运行看。
可以看到输出的只有两条,第一条数据和最后一条数据。
04Unlimited channel
代码语言:javascript复制val channel = Channel<String>(Channel.UNLIMITED)
当channel中后面参数加上Channel.UNLIMITED,然后再运行看。
这里就可以看出,当使用Channel.UNLIMITED时,完全是按钮协程调用的顺序输出的。
Kotlin使用协程时,还是会经常用Channel来处理协程之间的数据通信,更多的用法可以自己去多做尝试
完