依赖
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的特点

Channel在概念上有点类似于BlockingQueue, 元素从一端被加入, 从另一端被消费. 关键的区别在于, 读写的方法不是blocking的, 而是suspending的.
在为空或为满时. channel可以suspend它的sendreceive操作.

生成数据,消费数据的方法都需要在协程里执行

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

不同于Queuechannel可以被关闭,对于channel的关闭,我们可以使用close(),关闭前发射的值将仍然能在接收端收到,接收端通过for循环来遍历接收到的值。

如果执行了 close 以后,还调用 send 方法,就会崩溃
Android Coroutines Channels_kotlin channels
所以一种比较安全的做法是,在 send 之前,要判断一下channel 是否已经关闭了

//发送数据
GlobalScope.launch {
      if (!channel.isClosedForSend) {
          channel.send("item1 ${System.currentTimeMillis()}")
       }
}
  其他方法
channel.isClosedForSend  //发送通道是否关闭
channel.isClosedForReceive  //接收通道是否关闭
channel.isEmpty  //是否为空