Coroutines are a solution, a solution to the concept of nesting, concurrency, and weakened threads. It allows for better collaboration between multiple tasks, the ability to orchestrate code synchronously to do asynchronous work, and write asynchronous code as intuitively as synchronous code.

The essence of the key coroutine is the suspension and recovery of methods: return + callback

  • What is a coroutine:

The coroutine is a program that can be controlled by the program to suspend and resume. It can realize the cooperative execution of multi-task. It can be used to solve the flexible transfer of asynchronous task control flow

  • Functions of coroutines:

Coroutines can make asynchronous code synchronized coroutines can reduce the design complexity of asynchronous programs coroutines can’t make code asynchronous by themselves, they just make it easier to control the flow of asynchronous code

To sum up: Coroutines are all about suspending and resuming programs, handling asynchronous callbacks and synchronizing asynchronous code.

What is a coroutine

Kotlin standard library of coroutines: Kotlinx-Coroutines-core is the framework layer of coroutines, while Kotlin itself has the base layer of coroutines. If coroutine framework layer is encapsulated, dependent libraries need to be introduced to use the framework layer of coroutines.

 implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.4.2'
Copy the code

Common asynchronous callbacks: Create callback instances in enqueue to receive callbacks

retrofit.create(GitHubService::class.java).listRepos("octocat")
            .enqueue(object : Callback<List<Repo>> {
                override fun onResponse(call: Call<List<Repo>>, response: Response<List<Repo> >) {
                    println("retrofit:"+ response.body()? .size) }override fun onFailure(call: Call<List<Repo>>, t: Throwable) {
                    TODO("Not yet implemented")}})Copy the code

Asynchronous programs modified by coroutines:

  1. Let’s put it in front of the functionsuspendIs defined asHang up functionCoroutines are already supported in Retrofit 2.6 and above
    /** * kotlin coroutine */
    @GET("users/{user}/repos")
    suspend fun listReposKx(@Path("user") user: String?).: List<Repo>
Copy the code
  1. The coroutine approach requests the network, making it easier to write asynchronous code as synchronous code

Execution Result:

The suspension of a coroutine function requires a CoroutineScope space. The function suspension does not affect the execution of code outside the CoroutineScope space. Non-blocking suspension. Note: Suspend functions decorated by suspend can only be called by suspend functions or coroutines.

Coroutine run: Suspend function encounteredlistReposKxExecute on thread 2, return the result to continue execution.hangandrestore

Coroutines: Language implemented Coroutines that run on top of kernel threads

The basic elements of coroutines

Kotlin’s coroutine is divided into two layers:

  • Infrastructure layer: The coroutine API of the standard library, which provides the most basic conceptual and semantic support for coroutines
  • Business framework layer: Upper framework support for coroutines

In the code aboveGlobalScope.launchIt’s businessThe framework layerThe implementation of the. If you strip away the encapsulation of the framework layer, kotlin providesBase layerHow is the coroutine implemented? That’s what you need to knowThe basic elements of coroutines

The basic layer API of the Kotlin coroutine includes:Suspend suspends a function,Continuation,CreateCoroutine Creates coroutines,StartCoroutine Starts the coroutine,CoroutinContext Coroutine context. These five are the basic elements of coroutines

Suspend the function suspend

Suspend starting point: a function decorated by suspend that can only be called from other suspend functions or coroutines. Suspended function calls contain the “suspend” semantics of coroutines, and suspended function returns contain the “restore” semantics of coroutines.

Suspend and resume mainly through:ContinuationThe following code decommounts the suspend function added to the service, adding an additional parameter to the suspend functionContinuation.Actually in the abovecreateCoroutineIs passed oneContinuationInstance,ContinuationThe main function is to execute the “resume” code of the suspended function and return the value of the suspended function as an argument.

How can asynchronous callbacks be modified by suspending functions? In fact, isretrofitHow to deal withsuspendFunction, through kotlin’s base layer API code as follows: throughsuspendCoroutineTo implement the suspend function in passingContinuationReturns the result of the asynchronous callback requestRetrofitIt’s the same principle,RetrofitBased on theCallExtended methods are handled in the Service APIsuspendMethod is called on the basis ofRetrofitHis writingsuspendfunctionIn fact, for common framework layer encapsulation for examplexxxScope.launch: is implemented based on Kotlin’s base layer API

Coroutine context

ContinuationInterceptor is a class of CoroutineContext elements that can intercept continuations of coroutines and switch threads. The coroutine context contains a CoroutineDispatcher (see CoroutineDispatcher) that determines on which thread or threads the associated coroutine is executed. The coroutine scheduler can limit the execution of a coroutine to a specific thread, dispatch it to a thread pool, or let it run unchecked. All coroutine builders such as launch and async receive an optional CoroutineContext parameter, which can be used to explicitly specify a scheduler for a new coroutine or other context element.

The application of coroutine framework on Android

Kotlin’s framework layer is based on the encapsulation of the base layer: Kotlinx-Coroutines

Kotlin provides the base library of the coroutine framework as well as the Coroutine Android library

// Kotlin standard library - provides the base layer of coroutines
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
def coroutines_version = "1.4.2"
// The kotlin coroutine dependency library
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
// The coroutine Android library provides the AndroidUI scheduler
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
Copy the code

Extension libraries related to coroutines

There are explanations in the official documentation, which will not be repeated here, focusing on the application of coroutines in Android.

  • Job: startup mode of the coroutine
  • Scheduler: Thread scheduling of coroutines (executing coroutines in a specific thread or thread pool)
  • Scope: Scope of the coroutine (common in Android: lifecycleScope viewmodelScope)
  • Channel: “hot” data stream, a concurrent secure communication mechanism (sending data without subscribing)
  • Flow: “cold” data Flow, a responsive API for coroutines (similar to RxJava, data is sent only if subscribed)
  • Select: Multiple pending events can be waited on

Coroutines simplify Dialog

Create a suspend function on the Dialog that extends the alert method to the Context as follows:

Will create hang through suspendCancellableCoroutine function, asynchronous process synchronization

To call a suspended function via a coroutine:

LifecycleScope and Activity/Fragment lifecycle binding, you can directly get popover click results for processing

        val show_dialog = findViewById<Button>(R.id.show_dialog)
        show_dialog.setOnClickListener {
            lifecycleScope.launch {
                // The Activity lifecycle coroutine
                val myCheck = alert("Warning"."Do You Want is?")
                Log.e("TAG"."onCreate: $myCheck")}}Copy the code

Simplify calls to handlers

An asynchronous call to handler. post

Call as follows:

        lifecycleScope.launch {
            val handler = Handler(Looper.getMainLooper())
            val h1 = handler.run { "test"}
            Log.e("TAG"."onCreate: $h1") // test
            val h2 = handler.runDelay(1000) { "delay" }
            Log.e("TAG"."onCreate: $h2") // delay
        }
Copy the code

Coroutine: A solution that simplifies all asynchronous calls to synchronous ones.

Job startup mode

The scheduler

Encapsulation principle of Retrofit+ coroutine with LiveData

Coroutines exist only to solve the problem of asynchronous invocation, which Retrofit has already solved internally through coroutines. Coroutines are more about simplifying and encapsulating part of the business logic again by combining with LiveData and ViewModel.

Dependency libraries to introduce:

In analyzing The Josn Converter, Kotlin recommends Moshi for research.

    //retrofit
    api rootProject.depsLibs.retrofit
    api rootProject.depsLibs.logging_interceptor
    api rootProject.depsLibs.converter_gson

    // Coroutine correlation
    def coroutines_version = "1.4.2"
    // The kotlin coroutine dependency library
    api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
    // The coroutine Android library provides the AndroidUI scheduler
    api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
	api "Androidx. Core: the core - KTX: 1.3.2." "
    api "Androidx. Lifecycle: lifecycle - livedata - KTX: 2.3.1." "
    api "Androidx. Lifecycle: lifecycle - livedata - core - KTX: 2.3.1." "
Copy the code

The lifecycle management of network requests is managed by the following dependency libraries

LifecycleScope defines the Activity/Fragment lifecycle management coroutine to run suspend functions for network requests or manage suspend functions for network requests through the viewModelScope lifecycle coroutine (recommended). Note: Lifecycle is not the same as a ViewModel, a ViewModel can continue to exist when the screen rotates. Lifecycle and the nature of the ViewModel is explained here: How does the ViewModel manage view state and how does LiveData safely view data

    api "Androidx. Lifecycle: lifecycle - runtime - KTX: 2.3.1." "
    api "Androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.3.1." "
Copy the code

Frame layer design

NetWorkManager API design is the entry class of the network framework.

Network request return data structure design:

**ResponseBean** Serves as the underlying data structure of JSON

Through Kotlin’s sealed class, used to distinguish network state, specific designApiResonseMainly includesNetwork Abnormal Status,Null data state,Success status, service error status. Business error status for code. Need to be set according to the error handler above) ApiResponse Static methods are used to build the returned data in different states

There are two types of CREATE construction methods. The first one is to catch the network request system exception. The second one is to determine whether the status code is successful or not

Core class: RequestAction wraps network request actions through Kotlin’s DSL

  • API: Passes the suspend suspend function defined in Retrofit’s Service
  • LoadCache: Passes a function that loads cached data
  • SaveCache: Passes a function that implements cached data

Note: These key methods are all functions that pass DSL wrappers

For example, the code looks very comfortable

    @GET("users/{user}/repos")
    suspend fun getJson2(@Path("user") user: String): ResponseBean<List<Repo>>
    
    private val netApi = NetWorkManager.instance.create(Service::class.java)
    
	val requestLiveData = viewModelScope.requestLiveData<List<Repo>> {
            // Request network
            api { netApi.getJson2(name) }

            // Load the database cache
            loadCache { 
                // Database request to return the result to liveData
                _databaseLiveData
            }

            // Save the data to the database
            saveCache {
                // Save data to the database
                Log.e("TAG"."getRepo: ${it.size}")}}Copy the code

Here’s how Retrofit+LiveData is integrated via coroutines

The CoroutineScope is the parent class of all coroutine scopes, which is extended with a function called requestLiveData, which is provided by the extension dependency library to run suspended functions emit in LiveDataScope and api.invoke are both suspended functions

Exception handling design

Uniform exception class: uniform use of ResponseThrowable as all exception classes for network requests

IExceptionHandler is used to convert exceptions to ResponseThrowable, and appSuccessCode is used to define the success code for the business layer. If the exception code is not a success code, it is a service exception. If the exception is not a success code, it is a system exception

IAdapterHandler is used to handle the ResponseThrowable exception of the IExceptionHandler conversion

The NetManager API

  1. Initialization Settings

  1. The global exception handling implementation class is as follows, through the transformedResponseThrowableIn thecodeDetermine which exception is in

  1. Convert the exception to ResponseThrowable

DefaultExceptionHandler If not set addExceptionHandler will use the default exception to convert the class

  1. In the MVVM architecture modeViewModelIn the realization of network request, external throughrepoLiveDataListen for data. Here_repoLiveData useMediaorLiveData You can only listen for data changes and cannot change the data to prevent unexpected errors caused by external changes to the data

  1. View layer implementation listens to network data

All monitored data is defined in the framework layer ResultData directly get the status and other information, notice that all exceptions in the Error state have been converted to ResponseThrowable in the framework layer to determine the specific exception through the code in the custom exception