Cracking Kotlin coroutines (10) - Select articles
Keywords: Kotlin coroutine Select multiplexing
Select is not a new concept, we have seen it in IO multiplexing, and we have seen it in Java NIO. Next, I will introduce to you the Select of Kotlin coroutine.
Reuse multiple await
We have touched a lot of suspending functions before, so if I have such a scenario, two APIs obtain data from the network and local cache respectively, and which one is expected to return first will be used for display:
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) }
}
No matter which API Deferred
is await
value will be suspended. If you want to realize this requirement, you need to start two coroutines to call await
, which will make the problem more complicated.
Next we use select
to solve this problem:
GlobalScope.launch {
val localDeferred = getUserFromLocal(login)
val remoteDeferred = getUserFromApi(login)
val userResponse = select<Response<User?>> {
localDeferred.onAwait {
Response(it, true) }
remoteDeferred.onAwait {
Response(it, false) }
}
...
}.join()
As you can see, we did not call directly await
, but onAwait
called select
a callback registered in , no matter which one is called back first, select
the result in the corresponding callback will be returned immediately. Assuming that returns localDeferred.onAwait
first , then userResponse
the value of is Response(it, true)
, of course, because our local cache may not exist, so select
the result type of is Response<User?>
.
For this case itself, if the local cache is returned first, then we also need to obtain the network results to display the latest results:
GlobalScope.launch {
...
userResponse.value?.let {
log(it) }
userResponse.isLocal.takeIf {
it }?.let {
val userFromApi = remoteDeferred.await()
cacheUser(login, userFromApi)
log(userFromApi)
}
}.join()
Multiplexing multiple Channels
For Channel
multiple , it is similar:
val channels = List(10) {
Channel<Int>() }
select<Int?> {
channels.forEach {
channel ->
channel.onReceive {
it }
// OR
channel.onReceiveOrNull {
it }
}
}
For onReceive
, if Channel
is closed, select
an exception will be thrown directly; for onReceiveOrNull
if Channel
it is closed, it
the value of is null
.
SelectClause
How do we know which events can be select
called ? In fact, all events that can be select
typed are SelectClauseN
of type, including:
SelectClause0
: The corresponding event has no return value. For example,join
there is no return value, and the correspondingonJoin
is this typeonJoin
. The parameter of is a no-argument function:select<Unit> {
job.onJoin { log("Join resumed!") }
}
SelectClause1
: The corresponding event has a return value, and the precedingonAwait
andonReceive
are all such cases.SelectClause2
: The corresponding event has a return value, and an additional parameter is required, for example,Channel.onSend
there are two parameters, the first is aChannel
value of data type, indicating the value to be sent, and the second is the callback when the send is successful:List(100) { element ->
select<Unit> {
channels.forEach { channel ->
channel.onSend(element) { sentChannel -> log("sent on $sentChannel") }
}
}
}
in the consumer When the consumption efficiency is low, the data can be sent to whichever one can be sent for processing, and theonSend
second parameter of the parameter isChannel
the object .
Therefore, if you want to confirm whether the suspend function is supported select
, SelectClauseN
you .
summary
In coroutines, the semantics of Select are similar to Java NIO or Unix IO multiplexing. Its existence allows us to easily implement 1 to N, and whichever comes first will be processed. Although Select and Channel are closer to business development than the coroutine API of the standard library, I personally think that they are still relatively low-level API packages, and in practice, Flow API can also be used to solve them in most cases.
And this Flow API is completely a coroutine API for responsive programming. We can learn it just like RxJava, so see you in the next article~~~
Kotlin coroutine learning materials can be obtained for free by scanning the QR code below!
"The most detailed Android version of kotlin coroutine entry advanced combat in history"
Chapter 1 Introduction to the Basics of Kotlin Coroutines
● 协程是什么
● 什么是Job 、Deferred 、协程作用域
● Kotlin协程的基础用法
Chapter 2 Preliminary Explanation of Key Knowledge Points of Kotlin Coroutine
● 协程调度器
● 协程上下文
● 协程启动模式
● 协程作用域
● 挂起函数
Chapter 3 Exception Handling of Kotlin Coroutines
● 协程异常的产生流程
● 协程的异常处理
Chapter 4 Basic application of kotlin coroutines in Android
● Android使用kotlin协程
● 在Activity与Framgent中使用协程
● ViewModel中使用协程
● 其他环境下使用协程
Chapter 5 Network request encapsulation of kotlin coroutine
● 协程的常用环境
● 协程在网络请求下的封装及使用
● 高阶函数方式
● 多状态函数返回值方式