关闭后再发送会抛异常:
channel关闭后,channel中的数据仍然可以被接受,只有当channel中的数据消费完了,isClosedForReceive才为true.
suspend fun main() {
basics()
}
suspend fun basics() {
val channel = Channel(Channel.RENDEZVOUS)
// val channel = Channel(Channel.UNLIMITED)
// val channel = Channel(Channel.CONFLATED)
// val channel = Channel(Channel.BUFFERED)
// val channel = Channel(1)
//生产者 发
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
channel.send(i)
log(“sent”, i)
}
channel.close()
}
//消费者 收
val consumer = GlobalScope.launch {
while (!channel.isClosedForReceive) {
log(“receiving”)
val value = channel.receiveOrNull()
log(“received”, value)
}
}
producer.join()
consumer.join()
}
Channel(Channel.RENDEZVOUS ) 的方式是发一个收一个,边发边收,如果没有接受的,发送者会挂起等待,输出如下:
Channel(Channel.UNLIMITED ) 的方式是全部发送完毕,才会接收到,先发后收,发送者发送完就返回了,不管有没有接受者,输出如下:
Channel(Channel.CONFLATED ) 的方式是不管发了多少个,只能收到最后一个,也是发送完就返回了,不管有没有接受者,输出如下:
Channel(Channel.BUFFERED ) 的方式也是发送者发送完就返回了,不管有没有接受者,可以指定buffer大小,输出如下:
Channel(1) 的方式指定管道的容量大小,如果数据超过容量,发送者就会挂起等待,直到有接受者取走数据,发送者才发送下一批数据,
channel接受数据的时候可以直接当成迭代器使用:
suspend fun iterateChannel() {
val channel = Channel(Channel.UNLIMITED)
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
channel.send(i)
log(“sent”, i)
}
channel.close()
}
val consumer = GlobalScope.launch {
for (i in channel) {
log("received: ", i)
}
}
producer.join()
consumer.join()
}
suspend fun producer() {
val receiveChannel = GlobalScope.produce(capacity = Channel.UNLIMITED) {
for (i in 0…3) {
log(“sending”, i)
send(i)
log(“sent”, i)
}
}
val consumer = GlobalScope.launch {
for (i in receiveChannel) {
log("received: ", i)
}
}
consumer.join()
}
suspend fun consumer() {
val sendChannel = GlobalScope.actor(capacity = Channel.UNLIMITED) {
for (i in this) {
log("received: ", i)
}
}
val producer = GlobalScope.launch {
for (i in 0…3) {
log(“sending”, i)
sendChannel.send(i)
log(“sent”, i)
}
}
producer.join()
}
suspend fun broadcast() {
//下面几种都可以创建一个BroadcastChannel
//val broadcastChannel = BroadcastChannel(Channel.BUFFERED)
//val broadcastChannel = Channel(Channel.BUFFERED).broadcast()
val broadcastChannel = GlobalScope.broadcast {
for (i in 0…5) {
send(i)
}
}
//启动5个接受者,每个都能收到
List(5) { index ->
GlobalScope.launch {
val receiveChannel = broadcastChannel.openSubscription()
for (i in receiveChannel) {
log(“[#$index] received: $i”)
}
}
}.joinAll()
}
输出:
Task :ChannelsKt.main()
21:07:12:924 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 0
21:07:12:924 [DefaultDispatcher-worker-5 @coroutine#6] [#4] received: 0
21:07:12:924 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 0
21:07:12:925 [DefaultDispatcher-worker-4 @coroutine#5] [#3] received: 0
21:07:12:925 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 0
21:07:12:944 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 1
21:07:12:943 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 1
21:07:12:943 [DefaultDispatcher-worker-5 @coroutine#6] [#4] received: 1
21:07:12:944 [DefaultDispatcher-worker-4 @coroutine#5] [#3] received: 1
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 1
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#4] [#2] received: 2
21:07:12:945 [DefaultDispatcher-worker-8 @coroutine#3] [#1] received: 2
21:07:12:945 [DefaultDispatcher-worker-8 @coroutine#3] [#1] received: 3
21:07:12:945 [DefaultDispatcher-worker-7 @coroutine#4] [#2] received: 3
21:07:12:945 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 2
21:07:12:946 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 3
21:07:12:946 [DefaultDispatcher-worker-8 @coroutine#5] [#3] received: 2
21:07:12:946 [DefaultDispatcher-worker-8 @coroutine#5] [#3] received: 3
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 2
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 3
21:07:12:946 [DefaultDispatcher-worker-1 @coroutine#3] [#1] received: 4
21:07:12:946 [DefaultDispatcher-worker-3 @coroutine#2] [#0] received: 4
21:07:12:946 [DefaultDispatcher-worker-1 @coroutine#6] [#4] received: 4
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#5] [#3] received: 4
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#5] [#3] received: 5
21:07:12:947 [DefaultDispatcher-worker-2 @coroutine#3] [#1] received: 5
21:07:12:947 [DefaultDispatcher-worker-6 @coroutine#2] [#0] received: 5
21:07:12:947 [DefaultDispatcher-worker-2 @coroutine#6] [#4] received: 5
21:07:12:947 [DefaultDispatcher-worker-3 @coroutine#4] [#2] received: 4
21:07:12:947 [DefaultDispatcher-worker-3 @coroutine#4] [#2] received: 5
Select的使用场景是多个协程异步执行时,获取最先结束的那个协程结果返回,比如加载图片时,可能从网络获取,也可能从本地获取,这两种可能同时异步执行,使用Select就会优先获取返回比较快的本地结果展示,然后我们再去获取网络最新的更新即可。
使用例子:
val localDir = File(“localCache”).also { it.mkdirs() }
val gson = Gson()
fun CoroutineScope.getUserFromApi(login: String) = async(Dispatchers.IO){
gitHubServiceApi.getUserSuspend(login)
}
fun CoroutineScope.getUserFromLocal(login:String) = async(Dispatchers.IO){
File(localDir, login).takeIf { it.exists() }?.readText()?.let { gson.fromJson(it, User::class.java) }
}
fun cacheUser(login: String, user: User){
File(localDir, login).writeText(gson.toJson(user))
}
data class Response(val value: T, val isLocal: Boolean)
suspend fun main() {
val login = “test”
GlobalScope.launch {
val localDeferred = getUserFromLocal(login)
val remoteDeferred = getUserFromApi(login)
//val userResponse = Response(localDeferred.await(), true)
//select选择优先返回的结果
val userResponse = select<Response<User?>> {
localDeferred.onAwait { Response(it, true) }
remoteDeferred.onAwait { Response(it, false) }
}
userResponse.value?.let { log(it) } //获取结果显示 输出
//如果是本地的结果,重新请求,并缓存本地
userResponse.isLocal.takeIf { it }?.let {
val userFromApi = remoteDeferred.await()
cacheUser(login, userFromApi)
log(userFromApi)
}
}.join()
}
如果有多个异步请求同时返回,select会按顺序取第一个,想要随机的取可以使用selectUnbiased
select大括号中onAwait的写法等价于 await() 的写法,localDeferred.await()
,还有很多操作join
send
等都是一样的前面加on
例子:使用channel和select实现统计代码行数
val KotlinFileFilter = { file: File -> file.isDirectory || file.name.endsWith(“.kt”) }
data class FileLines(val file: File, val lines: Int) {
override fun toString(): String {
return “${file.name}: $lines”
}
}
suspend fun main() {
val result = lineCounter(File(“.”))
log(result)
}
suspend fun lineCounter(root: File): HashMap<File, Int> {
return Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1).asCoroutineDispatcher()
//使用use自动关闭资源
.use {
//withContext是一个挂起函数 返回值是最后一行表达式的值
withContext(it){
val fileChannel = walkFile(root)
//定义5个同时读取
val fileLinesChannels = List(5){
fileLineCounter(fileChannel)
}
resultAggregator(fileLinesChannels)
}
}
}
//创建生产者返回ReceiveChannel
fun CoroutineScope.walkFile(root: File): ReceiveChannel {
return produce(capacity = Channel.BUFFERED) {
fileWalker(root)
}
}
//递归过滤kotlin文件并发送文件
suspend fun SendChannel.fileWalker(file: File) {
if(file.isDirectory){
file.listFiles()?.filter(KotlinFileFilter)?.forEach { fileWalker(it) }
} else {
send(file)
}
}
//输入File 返回FileLines对象
fun CoroutineScope.fileLineCounter(input: ReceiveChannel): ReceiveChannel {
return produce(capacity = Channel.BUFFERED) {
for (file in input){
//统计行数
file.useLines {
send(FileLines(file, it.count())) //发送结果
}
}
}
}
suspend fun CoroutineScope.resultAggregator(channels: List<ReceiveChannel>): HashMap<File, Int> {
val map = HashMap<File, Int>()
channels.aggregate {
filteredChannels ->
//使用select返回最快统计的那一个
select<FileLines?> {
filteredChannels.forEach {
it.onReceiveOrNull {
log(“received: $it”)
it
}
}
} ?.let {
map[it.file] = it.lines
}
}
return map
}
//tailrec-递归优化 定义List<ReceiveChannel>的扩展函数,过滤掉已完成的
tailrec suspend fun List<ReceiveChannel>.aggregate(block: suspend (List<ReceiveChannel>) -> Unit) {
block(this)//消费一次
//从当前list中过掉isClosedForReceive=true的ReceiveChannel
filter { !it.isClosedForReceive }.takeIf { it.isNotEmpty() }?.aggregate(block)//递归
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
写在最后
对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。
![
文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等,需要这些文档资料的,直接点击我的GitHub免费领取~
大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频**
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-nRE32y72-1710667662476)]
写在最后
对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。
[外链图片转存中…(img-y7nzh3zX-1710667662476)]
[外链图片转存中…(img-bWDuGjAa-1710667662476)]
[外链图片转存中…(img-wR7Sycw9-1710667662477)]
[外链图片转存中…(img-4y6GC0gh-1710667662477)]
文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等,需要这些文档资料的,直接点击我的GitHub免费领取~