Android Coroutines Channels

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/117674222
本文出自【赵彦军的博客】

依赖

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操作.

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

Channel 分类

在这里插入图片描述

  • RENDEZVOUS :表示约会形式的等待,没有缓存使用,send调用后就会一直挂起,直到receive到达。也是 Channel 默认形式。
  • UNLIMITED:表示执行缓存无限容量,容量是 Int.MAX_VALUE,send调用后就存放在channel里直接返回,不管是否有receive。但是我们在使用时还是需要注意内存情况。
  • CONFLATED:表示保留最新,send调用后就存放在channel里直接返回,但是channel里只能存放最近一次 send 的值。
  • BUFFERED:表示执行缓存使用默认容量,默认是64。

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 方法,就会崩溃
在这里插入图片描述
所以一种比较安全的做法是,在 send 之前,要判断一下channel 是否已经关闭了

//发送数据
GlobalScope.launch {
      if (!channel.isClosedForSend) {
          channel.send("item1 ${System.currentTimeMillis()}")
       }
}

Channel的协程Buidler(SendChannel / ReceiveChannel)

我们在上面示例中可见,通过一个生产者协程producer和一个消费者协程consumer进行了数据的send和receive,而在官方框架中也专门为生产者协程和消费者协程提供了两个函数来构建出协程,它们就是produce和actor。

而且通过produce和actor函数启动的协程结束后都会自动关闭对应的Channel。

  • produce:启动一个生产者协程,返回ReceiveChannel。

  • actor:启动一个消息者协程,返回SendChannel(注意,actor函数目前框架中是被标为废弃)。

示例如下:
在这里插入图片描述
在这里插入图片描述

BroadcastChannel

前面介绍的Channel的所发送的数据只能被一个消费者消费,而如果需要一对多的话那就需要BroadcastChannel,它会像我们平时使用广播一样进行分发给所有订阅者。

另外需要注意的是,BroadcastChannel不支持RENDEZVOUS。

在这里插入图片描述

Select

Select一般是IO多路复用的概念,而在协程的Select则是用于挂起函数的多路复用。通俗一点表达就是可以同时进行多个挂起函数的调用,但最后只选择执行最快的挂起函数的返回结果。

在这里插入图片描述
在这里插入图片描述

其他方法

channel.isClosedForSend  //发送通道是否关闭
channel.isClosedForReceive  //接收通道是否关闭
channel.isEmpty  //是否为空
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值