Kotlin Coroutines (Coroutine) Completely Analytical Series:
Kotlin Coroutines (coroutine) complete analysis (a), introduction to the coroutine
Kotlin Coroutines (coroutine) complete parsing (2), deep understanding of the hang, recovery and scheduling of coroutine
Kotlin Coroutines (collaborative) fully parsed (3), encapsulates asynchronous callbacks, coroutine relationships, and cancellation of coroutines
Kotlin Coroutines (coroutine) complete parsing (four), coroutine exception handling
Kotlin Coroutines (coroutine) complete analysis (5), concurrency of coroutines
This article is based on Kotlin v1.3.0-rc-146, Kotlin-Coroutines v1.0.0-RC1
The concept of Coroutine (coroutine) introduced in Kotlin can help write asynchronous code and is still experimental. There are relatively few materials detailing the coroutine in China, so I intend to write a series of articles that Kotlin Coroutines has completely analyzed, hoping to help you better understand the coroutine. This is the first in a series of articles that provide a brief introduction to the features of the coroutine and some basic concepts. The main purpose of coroutines is to simplify asynchronous programming, so start with why you need coroutines to write asynchronous code.
Kotlin Coroutine was finally released, so I followed up with the latest official version to update the content of this article.
The most common scenario in asynchronous programming is to execute a complex task in the background thread. The next task depends on the execution result of the previous task, so you must wait for the previous task to complete before you can start execution. Look at the three functions in the code below, both of which depend on the execution result of the previous function.
fun requestToken(): Token {
// makes request for a token & waits
return token // returns result when received
}
fun createPost(token: Token, item: Item): Post {
// sends item to the server & waits
return post // returns resulting post
}
fun processPost(post: Post) {
// does some local processing of result
}
The operations in the three functions are time-consuming operations, so they cannot be run directly in the UI thread, and the latter two functions depend on the execution result of the previous function. The three tasks cannot be run in parallel. How to solve this problem?
A common practice is to use callbacks to encapsulate the tasks that need to be performed later as callbacks.
fun requestTokenAsync(cb: (Token) -> Unit) { ... }
fun createPostAsync(token: Token, item: Item, cb: (Post) -> Unit) { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
requestTokenAsync { token ->
createPostAsync(token, item) { post ->
processPost(post)
}
}
}
Callbacks in a scenario with only two tasks are very simple and practical. Many online request frameworks use the callback on the onSuccess Listener, but in the scenario of more than three tasks, there will be multiple layers of callback nesting, and it is inconvenient to handle exceptions. .
CompletableFuture introduced in Java 8 can concatenate multiple tasks to avoid multi-level nesting problems.
fun requestTokenAsync(): CompletableFuture<Token> { ... }
fun createPostAsync(token: Token, item: Item): CompletableFuture<Post> { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
requestTokenAsync()
.thenCompose { token -> createPostAsync(token, item) }
.thenAccept { post -> processPost(post) }
.exceptionally { e ->
e.printStackTrace()
null
}
}
The above code uses a connector to concatenate three tasks, the lastexceptionallyThe method can also handle exceptions uniformly, but only in Java 8 or above.
CompletableFuture's approach is somewhat similar to the Rx family of chained calls, which is currently the most recommended practice.
fun requestToken(): Token { ... }
fun createPost(token: Token, item: Item): Post { ... }
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
Single.fromCallable { requestToken() }
.map { token -> createPost(token, item) }
.subscribe(
{ post -> processPost(post) }, // onSuccess
{ e -> e.printStackTrace() } // onError
)
}
RxJava's rich operators, easy thread scheduling, and exception handling make most people happy, and I am, but there is no more concise and easy to write way?
Here's the code to use the Kotlin coroutine:
Suspend fun requestToken(): Token { ... } // Suspend function
Suspend fun createPost(token: Token, item: Item): Post { ... } // Suspend function
fun processPost(post: Post) { ... }
fun postItem(item: Item) {
GlobalScope.launch {
val token = requestToken()
val post = createPost(token, item)
processPost(post)
/ / Need exception handling, directly add try / catch statement
}
}
The code after using the coroutine is very concise, writing asynchronous code in a sequential manner, does not block the current UI thread, and error handling is as simple as normal code.
dependencies {
// Kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
// Kotlin Coroutines
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0-RC1'
}
First look at the description of the official document:
Coroutines simplify asynchronous programming by putting complexity into the library. The logic of the program can be expressed sequentially in the coroutine, and the underlying library will solve its asynchrony for us. The library wraps the relevant parts of the user code as callbacks, subscribes to related events, schedules execution on different threads (even different machines), and the code remains as simple as sequential execution.
The editor of the coroutine, Roman Elizarov, describes the coroutine:Coroutines are like very lightweight threads. Threads are scheduled by the system, and the overhead of thread switching or thread blocking is relatively large. The coroutine depends on the thread, but the coroutine does not need to block the thread when it hangs, almost no cost, the coroutine is controlled by the developer. and soThe coroutine is also like the thread of the user state.Very lightweight, you can create any number of coroutines in a thread.
In summary: coroutines simplify asynchronous programming and can express programs sequentially, and coroutines provide a way to avoid blocking threads and replace thread blocking with cheaper, more controllable operations -- coroutine hangs.
Here are some basic concepts in the coroutine through the examples of the coroutine above:
Suspend fun requestToken(): Token { ... } // Suspend function
Suspend fun createPost(token: Token, item: Item): Post { ... } // Suspend function
fun processPost(post: Post) { ... }
requestTokenwithcreatePostIn front of the functionsuspendThe modifier tag, which means that both functions are pending functions. The suspend function can get the parameters and return values in the same way as normal functions, but the calling function may suspend the coroutine (if the result of the related call is already available, the library can decide to proceed without hanging),When the suspend function suspends the coroutine, it does not block the thread where the coroutine is located.. The coroutine will be resumed after the suspend function is executed, and the following code will continue to execute. However, suspend functions can only be called in coroutines or other suspend functions. In fact, to start a coroutine, there must be at least one suspend function, which is usually a pending lambda expression. and sosuspendModifiers can mark ordinary functions, extension functions, and lambda expressions.
Suspended functions can only be called in coroutines or other suspend functions, in the above examplelaunchThe function creates a coroutine.
fun postItem(item: Item) {
GlobalScope.launch { // Create a new coroutine
val token = requestToken()
val post = createPost(token, item)
processPost(post)
/ / Need exception handling, directly add try / catch statement
}
}
launchfunction:
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
From the above function definition, you can see some important concepts of coroutine: CoroutineContext, CoroutineDispatcher, Job. Let's introduce these concepts one by one.
CoroutineScope, which can be understood as the coroutine itself, contains the CoroutineContext.
CoroutineContext, a coroutine context, is a collection of elements, mainly including the Job and CoroutineDispatcher elements, which can represent a scene of a coroutine.
EmptyCoroutineContext represents an empty coroutine context.
CoroutineDispatcher, the coroutine scheduler, determines the thread or thread pool where the coroutine is located. It can specify a coroutine to run on a particular thread, a thread pool, or not specify any threads (so the coroutine will run on the current thread).coroutines-coreThere are three standard implementations in CoroutineDispatcherDispatchers.Default、Dispatchers.IO,Dispatchers.MainwithDispatchers.UnconfinedUnconfined does not specify a thread.
launchFunction definition if not specifiedCoroutineDispatcherOr no otherContinuationInterceptorThe default coroutine scheduler isDispatchers.Default,DefaultIs a coroutine scheduler whose specified thread is a shared thread pool with at least 2 threads and the same number of CPUs.
Job, task, encapsulates the code logic that needs to be executed in the coroutine. Job can be canceled and has a simple lifecycle, which has three states:
| State | [isActive] | [isCompleted] | [isCancelled] |
|---|---|---|---|
| New (optional initial state) | false |
false |
false |
| Active (default initial state) | true |
false |
false |
| Completing (optional transient state) | true |
false |
false |
| Cancelling (optional transient state) | false |
false |
true |
| Cancelled (final state) | false |
true |
true |
| Completed (final state) | false |
true |
false |
Job does not return a value when it completes. If you need to return a value, you should use Deferred, which is a subclass of Job.public interface Deferred<out T> : Job。
CoroutineScope.launchThe function belongs to the Coroutine builders, and there are several other Builders in Kotlin that are responsible for creating coroutines.
CoroutineScope.launch {} Is the most commonly used Coroutine builders, does not block the current thread, create a new coroutine in the background, you can also specify a coroutine scheduler, such as commonly used in AndroidGlobalScope.launch(Dispatchers.Main) {}。
fun postItem(item: Item) {
GlobalScope.launch(Dispatchers.Main) { // Create a new coroutine in the UI thread
val token = requestToken()
val post = createPost(token, item)
processPost(post)
}
}
runBlocking {}Is to create a new coroutine while blocking the current thread until the end of the coroutine. This should not be used in coroutines, mainly formainFunction and test design.
fun main(args: Array<String>) = runBlocking { // start main coroutine
launch { // launch new coroutine in background and continue
delay(1000L)
println("World!")
}
println("Hello,") // main coroutine continues here immediately
delay(2000L) // delaying for 2 seconds to keep JVM alive
}
class MyTest {
@Test
fun testMySuspendingFunction() = runBlocking {
// here we can use suspending functions using any assertion style that we like
}
}
withContext {}A new coroutine is not created, the pending code block is run on the specified coroutine, and the coroutine is suspended until the code block runs.
CoroutineScope.async {}Can achieve the same effect as the launch builder, create a new coroutine in the background, the only difference is that it has a return value, becauseCoroutineScope.async {}The returned type is Deferred.
fun main(args: Array<String>) = runBlocking { // start main coroutine
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() } // start async one coroutine without suspend main coroutine
val two = async { doSomethingUsefulTwo() } // start async two coroutine without suspend main coroutine
println("The answer is ${one.await() + two.await()}") // suspend main coroutine for waiting two async coroutines to finish
}
println("Completed in $time ms")
}
ObtainCoroutineScope.async {}The return value needs to passawait()The function, which is also a suspend function, suspends the current coroutine when it is called until the code in async is executed and returns a value.
The Kotlin coroutine can greatly simplify asynchronous programming. Although it is difficult to learn when you first get in touch, you will definitely fall in love with it after a period of contact. It is recommended that you also go through the recommended information below to get a quicker idea of the coroutine.
Recommended reading:
Introduction to Coroutines(Roman Elizarov at KotlinConf 2017, slides)
Official documentation:http://kotlinlang.org/docs/reference/coroutines.html 1. Coroutine concept and role (Coroutines) 2. The difference between thread blocking and coroutine hang (Blocking VS Suspend...
Why can't 80% of programmers be architects? >>> The previous article "A Preliminary Study of Kotlin Coroutine" introduced you to the origin, important concepts, and usag...
As a Java language user, using kotlin has been more than a year. Privately think that kotlin is a decorator of the Java language: it is similar to the Java language in the large structure, and adds ma...
Foreword I recently researched the Kotlin coroutine and found that the functionality is really powerful, useful, and very good. If you are planning or using Kotlin to develop Android, you must not mis...
Welcome to pay attention to program gravity I have recently been fortunate to participate in a seminar with Kotlin core author Hadi Hariri. As the leader of the JetBrains developer promotion team, Mr....
Articles directory Foreword 1. Council basis First coroutine Structured concurrency Hanging function SUSPEND Range constructor Scope Builder The range constructor Scope Builder and concurrency job Lig...
1 What is a coroutine? Coroutine was translated into a "coroutine", meaning that each sub-task runs collaboratively, so everyone understands that it was created to solve the asynchronous pro...
Kotlin Coroutine series: 1. Introduction to Kotlin Coroutine 2. Kotlin Coroutine (coroutine) basics This article focuses on some of the basic concepts in the coroutine. Suspend function (suspend keywo...
Foreword Kotlin is now a fresher language. I have asked friends around me, and some seem to start using them to start writing backgrounds, and some start to refactor Android project code with kotlin. ...
Reference article: https://kaixue.io/kotlin-coroutines-1/ https://johnnyshieh.me/posts/kotlin-coroutine-introduction/ The concept of coroutine in Kotlin The concept of kotlin coroutine is not p...