An introduction to coroutines

  • Examine knowledge of coroutines from an interview perspective:
  1. Coroutines are lightweight threads. Why lightweight? I’ll tell you the conclusion first, because it’s based on the thread pool API, so it’s really good at dealing with concurrent tasks.
  2. It’s based on a thread pool. Wouldn’t it be better if I just use the thread pool or use some of the other asynchronous task solutions in Android, such as Handler, RxJava, etc. (This is worth thinking about)
  3. Coroutines have the greatest advantage of being able to write non-blocking code in a blocking manner, solving the callback hell common in concurrency.
  • To understand their relationship from an Android developer’s perspective:
  1. All of our code runs in threads, and threads run in processes.
  2. Coroutines are not directly associated with the operating system, but they are not castles in the air. They also run in threads, either single-threaded or multi-threaded.
  3. The total execution time of a coroutine in a single thread is not less than without a coroutine.
  4. Android system, if the requests for the network on the main thread, throws NetworkOnMainThreadException, for coroutines on the main thread is not exceptional also, this scenario using coroutines or to cut the thread.
  5. When we look at coroutines in Kotlin, we can really start from the perspective of thread control. Because in Kotlin, a typical use scenario for coroutines is thread control. Just like Executor in Java and AsyncTask in Android, coroutines in Kotlin encapsulates the Thread API, making it easy to write concurrent operations without having to worry about multiple threads.

I met coroutines

  • First of all, it is important to know what the so-called Kotlin coroutine looks like.
fun main(args: Array<String>) { launch(CommonPool) { delay(1000L) println("World!" } println("Hello,") thread.sleep (2000L)} /* Will print) Hello, World! * /Copy the code

Regardless of the details, the above code looks like this:

  1. Main process:

Call the system’s launch method to launch a coroutine, followed by curly braces that can be seen as the body of the coroutine. Print “Hello,” to the main thread sleep for two seconds (sleep here just keeps the process alive). The purpose is to wait for the coroutine to finish executing.)

  1. Coroutine flow:
  • Coroutine delays 1 second
  • Print out the “World!”

Explain the delay method: Delay is the same as sleep in a coroutine, but it doesn’t block the current thread. It’s like setting an alarm. Before the alarm goes off, the thread running the coroutine can be scheduled to do something else, and when the alarm rings, the coroutine resumes.

Coroutines can also disappear after starting

  • The launche method has a return value of type Job, and the Job has a cancel method to take the elimination coroutine. Let’s take a look at a simple example of counting sheep
fun main(args: Array<String>) { val job = launch(CommonPool) { var i = 1 while(true) { println("$i little sheep") ++i delay(500L) // }} Thread. Sleep (1000L); job. Cancel (); Thread.sleep(1000L) println("main process finished.")}Copy the code
  • The result of its operation is:
1 little sheep
2 little sheep
main process finished.
Copy the code

The above is only a brief introduction to the use of Kotlin coroutine, specific can refer to the following information, in-depth understanding

  • Kotlin minimalism tutorial
  • Kotlin programming – coroutines

Reference coroutines in Android

Step 1: Import libraries. Android needs to import the following two libraries

Implementation "org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.3.2" implementation "Org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.2." "Copy the code

Declare the CoroutineScope scope

It is recommended that lifecycle classes inherit from CoroutineSocpe so that all coroutines end with the lifecycle

  • Such as in an activity
MainActivity : AppCompatActivity(), CoroutineScope by MainScope(){
    override fun onDestroy(){
        super.onDestory()
        cancel()
     }
}  
Copy the code
  • Used in UI logic classes
class MainActivityFacede : CoroutineScope {
    private val job = Job()

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    fun destroy() {
        job.cancel()
    }
}
Copy the code

The above code cancles the coroutine running in this scope when it calls destroy

Run coroutines

  1. So let me just say there are two types of running coroutines, launch and async.

Because we are to say fast use, so I do not withhold source code with everyone here. (Whisper beep, source code to see a headache)

  1. Briefly, the differences are as follows:
  • Launch does not return a value, or returns only a job that knows the status of the task but does not return results.
  • Async has a return value, that is, it returns Deferred, it’s an inherited job, it has all the jobs that jobs have, and it has the ability to bring back data that jobs don’t have.
  • Launch can be used to run coroutines that do not require action results (e.g. file deletion, creation, etc.)
  • Async can be used to run asynchronous time-consuming tasks that require a return value (network requests, database operations, file reads and writes, etc.).
  1. Briefly add life cycles for jobs and Deferred
State isActive isConpleted isCanceled
new false false false
active true false false
completing true false false
canceling false true true
completed false true false

use

  • Methods a
private suspend fun getWebTime(): Long = withContext(Dispatchers.IO) { var result = RequeastTest.getInstance().start() val name = Thread.currentThread().name if (! coroutines.contains(name)) { coroutines.add(name) } result } launch() { //do sth var time = getWebTime() //update UI } Launch {var deferred = async() {// Initiate network request.. getWebTime() } //do sth ... var value = deferred.await() //do sth... }Copy the code
  • Way 2
private suspend fun getWebTime(): Long { var result = RequeastTest.getInstance().start() val name = Thread.currentThread().name if (! coroutines.contains(name)) { coroutines.add(name) } return result } launch() { //do sth var time = WithContext (dispather.io){getWebTime()} //update UI} launch {var deferred = async(dispather.io){// Initiate network request.. getWebTime() } //do sth ... var value = deferred.await() //do sth... }Copy the code

From the above two ways, you can see the use of new things Dispather and Suspend.

  1. Tell me about Dispther
  • Can be thought of as a coroutine scheduler, can be used to schedule the coroutine to run in which thread
  • The following values are described
value instructions
Dispathers.Default Share threads in the background thread pool
Dispathers.Main AndroidUi thread
Dispathers.IO Threads that share the background thread pool
Dispthers.Uniconfined Threads that use the parent coroutine are not restricted
newSingleThreadContext Using new threads
  • Dispather can be used to specify which thread to run the coroutine on launch, async, etc., or in the coroutine withContext(Dispather.)

To switch threads. When you switch threads withContext, you return a value.

  1. Suspend
  • The only modifier in the coroutine, which is used to modify the function, indicates that the function is a suspended function, and that the coroutine compiler performs the CPS transform during compilation to do something undescribable (see the official documentation for details).
  • Functions decorated with suspend can only be called from the body of a coroutine and functions that also use suspend.

The difference between the two is the way the getWebTime function is written, which results in a difference in the way the coroutine is written (the scheduling thread is written differently).

  • In addition, you can need to carry out multiple network requests at the same time, and organize the data after all the requests are completed. When rendering, async is more convenient than anything, as follows:
Launch {var userInfoDeferred = async{// Get user base information getUserInfo(aid)} var userTeamsDeferred = async{// Get user team.. } var userInfo = userInfoDeferred.await() getUserTeams(AID)} var userInfo = userInfoDeferred = async {// getUserOrgs(aid)} var userInfo = userInfoDeferred. Await () Var userTeams = userTeamsDeferred. Await () var userOrgsDeferred = userOrgsDeferred.await() // Render UI}Copy the code

Ps: If you need to know more, please refer to the official website document

conclusion

  • The above summarizes the simple understanding and use of coroutines. It is difficult to give a concrete definition of what a coroutine is. Even if it can be given, it will be very abstract and difficult to understand.
  • Coroutines, on the other hand, can be described as compiler capabilities, because coroutines do not require OS or hardware support (threads do), but the compiler provides some keywords and internally generates some supporting code (possibly bytecode) to make it easier for developers to write code.
  1. Here’s my personal summary of coroutines:
  • First, a coroutine is a block of code containing specific logic that can be scheduled to execute on a different thread.
  • Second, a coroutine is an environment in which a method can be waited for execution and then returned with the result. During the wait, the thread resources that host the coroutine can be used elsewhere.
  • Third, a coroutine is a logical process that is independent of the running process. The steps in a coroutine, whether synchronous or asynchronous, are linear (done in sequence from front to back).

Ps: Are there any flaws, welcome students to point out