依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'
channels 可以干啥
channels
用于协程间的通信, 允许我们在不同的协程间传递数据(a stream of values).
生产者-消费者模式
发送数据到channel
的协程被称为producer
, 从channel
接受数据的协程被称为consumer
.
当需要的时候, 多个协程可以向同一个channel
发送数据, 一个channel
的数据也可以被多个协程接收.
当多个协程从同一个channel
接收数据的时候, 每个元素仅被其中一个consumer
消费一次. 处理元素会自动将其从channel
里删除.
Channel
在概念上有点类似于BlockingQueue
, 元素从一端被加入, 从另一端被消费. 关键的区别在于, 读写的方法不是blocking
的, 而是suspending
的.
在为空或为满时. channel
可以suspend它的send
和receive
操作.
生成数据,消费数据的方法都需要在协程里执行
receive在 Channel
有数据时接收数据,在 Channel
为空时处于挂起状态,时刻准备接收数据。一旦接收完数据后,就执行完了
class MainActivity : AppCompatActivity() {
private val channel = Channel<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.btn1).setOnClickListener {
//发送数据
GlobalScope.launch {
channel.send("item1 ${System.currentTimeMillis()}")
}
}
//接收数据
GlobalScope.launch {
repeat(10) {
Log.d("channel", "receive--${channel.receive()}")
}
}
}
consumeEach
class MainActivity : AppCompatActivity() {
private val channel = Channel<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.btn1).setOnClickListener {
//发送数据
GlobalScope.launch {
channel.send("item1 ${System.currentTimeMillis()}")
}
}
//接收数据
GlobalScope.launch {
channel.consumeEach {
Log.d("channel", "consumeEach--${it}")
}
}
}
}
//输出结果
D/channel: consumeEach--item1 1623123193526
consumeEach
会在接收到数据后,从把数据从队列中移除。其他接收者,就不会再次接收了。也就是同一个数据,只能被一个数据接收
kotlin
为我们提供了一个简单的 channel
的遍历方法,也就是 for
循环。
遍历 channel
会一直处于挂起状态,只要有数据,就会全部遍历一次。没有数据,处于挂起状态。
class MainActivity : AppCompatActivity() {
private val channel = Channel<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<View>(R.id.btn1).setOnClickListener {
//发送数据
GlobalScope.launch {
repeat(100) {
channel.send("item1 $it")
}
}
}
//接收数据
GlobalScope.launch {
for (i in channel) {
Log.d("channel", "$i")
}
}
}
}
close
不同于Queue
,channel
可以被关闭,对于channel
的关闭,我们可以使用close()
,关闭前发射的值将仍然能在接收端收到,接收端通过for
循环来遍历接收到的值。
如果执行了 close 以后,还调用 send 方法,就会崩溃
所以一种比较安全的做法是,在 send
之前,要判断一下channel
是否已经关闭了
//发送数据
GlobalScope.launch {
if (!channel.isClosedForSend) {
channel.send("item1 ${System.currentTimeMillis()}")
}
}
其他方法
channel.isClosedForSend //发送通道是否关闭
channel.isClosedForReceive //接收通道是否关闭
channel.isEmpty //是否为空