Because the article involves only a lot of points, the content may be too long, according to their own ability level and familiarity with the stage jump read. If there is an incorrect place to tell you trouble private letter to the author, very grateful

Due to time reasons, the author works during the day and can only write in the free time at night, so the update frequency should be once a week. Of course, I will try my best to make use of time and try to publish in advance. In order to facilitate reading, this article is divided into multiple chapters, and the corresponding chapters are selected according to their own needs. Now it is only a general catalog in the author’s mind, and the final update shall prevail:

  • Basic usage of Kotlin coroutines
  • Kotlin coroutine introduction to Android version in detail (ii) -> Kotlin coroutine key knowledge points preliminary explanation
  • Kotlin coroutine exception handling
  • Use Kotlin coroutine to develop Android applications
  • Network request encapsulation for Kotlin coroutines
  • [Kotlin Coroutine for Android (6) -> Kotlin coroutine combined with Jetpack and the use of third-party frameworks]
  • [Kotlin coroutine introduction for Android (7) -> In-depth Kotlin Coroutine principle (1)]
  • [Kotlin coroutine introduction for Android version in detail (8) -> In-depth Kotlin coroutine principle (2)]
  • [Kotlin coroutine introduction for Android version in detail (9) -> In-depth Kotlin coroutine principle (3)]
  • [Kotlin coroutine introduction for Android (10) -> In-depth Kotlin coroutine principle (4)]

Extend the series

  • Encapsulating DataBinding saves you thousands of lines of code
  • A ViewModel wrapper for everyday use

The basic application of Kotlin coroutine in Android

Through the previous three chapters, we have now understood the basic use of Kotlin coroutines and related basic knowledge. Such as:

  1. Basic usage and principles of coroutines.
  2. CoroutineContext: contained in the coroutine contextElementAnd below the role of transmission.
  3. CoroutineDispatcher: Use of coroutine scheduler
  4. CoroutineStart: The difference between coroutine startup modes in different modes
  5. CoroutineScope: Classification of coroutine scope, and exception handling under different scope.
  6. Suspend functions as wellsuspendThe role of keywords, as wellContinuationThe suspend recovery process of.
  7. CoroutineExceptionHandler: Coroutine exception handling, combiningsupervisorScopeandSupervisorJobThe use of.

In this section, we will focus on the basic use of Kotlin coroutines in Android. Let’s start with the related extension library component library:

    implementation "Androidx. Activity: activity - KTX: 1.2.2." "
    implementation "Androidx. Fragments: fragments - KTX: 1.3.3." "
Copy the code

Android uses Kotlin coroutines

The way we used coroutines in the previous sections was to use runBlocking or GlobalScope launch and async. You can also create a new coroutine and launch a new coroutine via launch or async. We in the interpretation of coroutines exception handling is mentioned in chapter, through SupervisorJob and CoroutineExceptionHandler implements a and supervisorScope the same scope.

private fun testException(a){
    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        Log.d("exceptionHandler"."${coroutineContext[CoroutineName].toString()}Handling exceptions:$throwable")}val supervisorScope = CoroutineScope(SupervisorJob() + exceptionHandler)
    with(supervisorScope) {
        launch{
        }
        / / to omit...}}Copy the code

In the first section we mentioned runBlocking, which links regular blocking code together and is used primarily in main functions and tests. GlobalScope, in turn, is a global top-level coroutine, which we have used in most of our previous cases. But the coroutine runs for the entire application life cycle, and if we started one with GlobalScope, it would be extremely tedious to start one and would require handling references and managing exception cancellations.

We can ignore the CoroutineExceptionHandler coroutines exception handling. Because no matter any way start coroutines, if not add CoroutineExceptionHandler process context, when producing an uncaught exception application can result in collapse.

So what’s wrong with the following code?

private fun start(a) {
    GlobalScope.launch{
        launch {
            // Network request 1...
            throw  NullPointerException(Null pointer)}val result = withContext(Dispatchers.IO) {
            // Network request 2...
            requestData()
            "Request result"
        }
         btn.text = result
        launch {
            // Network request 3...}}}Copy the code
  • Because our GlobalScope uses Dispatchers.Default, this causes us to refresh the UI on the off-main thread.

  • Subcoroutines produce exceptions and interfere with each other. Child coroutine exception cancellation causes the parent coroutine to cancel and other child coroutines to cancel.

  • If we exit the activity or Framgent at this point, because the coroutine is running inside the GlobalScope, the coroutine will still be running even if the Activity or Framgent exits, and that will cause all kinds of leaks. At the same time, when the coroutine performs the refresh operation, the UI refresh will crash because our interface has been destroyed.

If we’re going to solve the above problem. Here’s what we have to do:

var job:Job? = null
private fun start(a) {
    job = GlobalScope.launch(Dispatchers.Main + SupervisorJob()) {
        launch {
            throw  NullPointerException(Null pointer)}val result = withContext(Dispatchers.IO) {
            // Network request...
            "Request result"
        }
        launch {
            // Network request 3...
        }
        btn.text = result
    }
}

override fun onDestroy(a) {
    super.onDestroy() job? .cancel() }Copy the code

We need to add Dispatchers.Main at launch to make sure we’re refreshing the UI on the Main thread, and we need to add container container container job in the GlobalScope. Launch coroutine context to prevent the whole coroutine tree from being terminated due to abnormal cancellation of the subcoroutine. Finally, we need to save every Job started through GlobalScope and cancel the entire coroutine tree by calling job.Cancel when the activity or Framgent exits. It feels ok to do it again, but we are not writing it once, each time we write it will not feel super troublesome, even doubt life.

So the official Kotlin coroutine provides a coroutine that runs on the main thread by default: MainScope, which we can use to start the coroutine.

public fun MainScope(a): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
Copy the code

We can see that MainScope is created using the container container for SupervisorJob and Dispatchers.main by default. UI component refreshes can be handled by MainScope. Meanwhile, since MainScope is designed in SupervisorJob, the cancellation operations caused by anomalies in our sub-coroutines will not lead to the cancellation of MainScope. This simplifies the process of starting a coroutine through GlobalScope.

private val mainScope = MainScope()
private fun start(a) {
    mainScope.launch {
        launch {
            throw  NullPointerException(Null pointer)}val result = withContext(Dispatchers.IO) {
            // Network request...
            "Request result"
        }
        launch {
            // Network request 3...
        }
        btn.text = result
    }
} 
override fun onDestroy(a) {
    super.onDestroy()
    mainScope.cancel()
}
Copy the code

By using MainScope we omit a lot of operations. Instead of saving every Job started by MainScope, we call mainscope.cancel () at the end of the destruction to cancel all coroutines started by MainScope.

One more note: Some of you may be wondering if GlobalScope doesn’t save the started Job, but globalScope.Cancel. If so, congratulations on the super crash BUG. I’m not going to extend it here. You can try yourself, after all, the truth comes from practice.

I don’t even bother to write MainScope, and I often forget how to call MainScope to cancel.

The authorities already have a solution for us lazy people, we just need to integrate another KTX runtime.

Use coroutines in Activity and Framgent

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

At this point we can start the coroutine directly using lifecycleScope in the Activity or Framgent. Let’s look at the lifecycleScope implementation of the activity

public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
Copy the code

We can go to lifecycleScope which is going to get a coroutineScope through Lifecycle, which is a LifecycleCoroutineScope object.

public val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = mInternalScopeRef.get(a)as LifecycleCoroutineScopeImpl?
            if(existing ! =null) {
                return existing
            }
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }
Copy the code

LifecycleScope uses the same approach as MainScope to create a CoroutineScope, but also uses lifecycle to automatically close all coroutines when Lifecycle is in a DESTROYED state.

public abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {
    internal abstract val lifecycle: Lifecycle
    public fun launchWhenCreated(block: suspend CoroutineScope. () - >Unit): Job = launch {
        lifecycle.whenCreated(block)
    }
    public fun launchWhenStarted(block: suspend CoroutineScope. () - >Unit): Job = launch {
        lifecycle.whenStarted(block)
    }
    public fun launchWhenResumed(block: suspend CoroutineScope. () - >Unit): Job = launch {
        lifecycle.whenResumed(block)
    }
}


internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }

    fun register(a) {
        launch(Dispatchers.Main.immediate) {
            if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)}else {
                coroutineContext.cancel()
            }
        }
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}
Copy the code

We can also start coroutines by launchWhenCreated, launchWhenStarted, launchWhenResumed, and when Lifecycle is in the appropriate state, the coroutine that is created here will be automatically triggered.

For example, we can do this:

class MainTestActivity : AppCompatActivity() {
    init {
        lifecycleScope.launchWhenResumed {
            Log.d("init"."Start the coroutine at class initialization location")}}override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  }
}
Copy the code
D/onResume: onResume
D/init: starts the coroutine at the class initialization locationCopy the code

In our normal loading order, should init execute output first? In practice, however, it waits for the Activity to enter the onResume state before it executes and then looks at the whenResumed implementation of the call in launchWhenResumed.

public suspend fun <T> Lifecycle.whenResumed(block: suspend CoroutineScope. () - >T): T {
    return whenStateAtLeast(Lifecycle.State.RESUMED, block)
}

public suspend fun <T> Lifecycle.whenStateAtLeast(
    minState: Lifecycle.State,
    block: suspend CoroutineScope. () - >T
): T = withContext(Dispatchers.Main.immediate) {
    valjob = coroutineContext[Job] ? : error("when[State] methods should have a parent job")
    val dispatcher = PausingDispatcher()
    val controller =
        LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
    try {
        withContext(dispatcher, block)
    } finally {
        controller.finish()
    }
}

@MainThread
internal class LifecycleController(
    private val lifecycle: Lifecycle,
    private val minState: Lifecycle.State,
    private val dispatchQueue: DispatchQueue,
    parentJob: Job
) {
    private val observer = LifecycleEventObserver { source, _ ->
        if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
            handleDestroy(parentJob)
        } else if (source.lifecycle.currentState < minState) {
            dispatchQueue.pause()
        } else {
            dispatchQueue.resume()
        }
    }
    
    init {
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            handleDestroy(parentJob)
        } else {
            lifecycle.addObserver(observer)
        }
    }
    
    private inline fun handleDestroy(parentJob: Job) {
        parentJob.cancel()
        finish()
    }
    
    @MainThread
    fun finish(a) {
        lifecycle.removeObserver(observer)
        dispatchQueue.finish()
    }
}
Copy the code

We can see that we are actually calling whenStateAtLeast and using withContext to perform a synchronous operation. LifecycleObserver will then be added to LifecycleController to monitor the state. Lifecycle will be used to compare the current state with the trigger state that we set and will decide whether to resume execution.

We now have an overview of the creation and destruction process of lifecycleScope in an Activity. Similarly, lifecycleScope in fragments is implemented in the same way as an Activity, so we won’t repeat the explanation here. Let’s do a simple experiment:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        lifecycleScope.launch {
            delay(2000)
            Toast.makeText(this@MainActivity."haha",Toast.LENGTH_SHORT).show()
        }
    }
}
Copy the code

Isn’t this much easier than before, we don’t have to worry about the creation process, we don’t have to worry about the destruction process.

This time we need to mention CoroutineExceptionHandler coroutines exception handling. Through the previous chapter we know that start a collaborators cheng later, if you do not add CoroutineExceptionHandler cases in coroutines context, once produced an uncaught exception, so our program will collapse.

class MainActivity : AppCompatActivity() {
    val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->
        Log.d("exceptionHandler"."${coroutineContext[CoroutineName]} $throwable")}fun load(a) {
        lifecycleScope.launch(exceptionHandler) {
           / / to omit...
        }
         lifecycleScope.launch(exceptionHandler) {
           / / to omit...
        }
         lifecycleScope.launch(exceptionHandler) {
           / / to omit...}}}Copy the code

When that happens, people like this writer, who suffer from severe laziness, start to freak out. Why to write so many times lifecycleScope launch, at the same time, every time you start to manually add CoroutineExceptionHandler. Can’t it be simpler?

Yes, of course. First we define a custom exception handling, we will implement a simple exception log output:

/ * * *@paramErrCode Indicates an error code *@paramErrMsg Brief error message *@paramReport Whether to report */
class GlobalCoroutineExceptionHandler(private val errCode: Int.private val errMsg: String = "".private val report: Boolean = false) : CoroutineExceptionHandler {
    override val key: CoroutineContext.Key<*>
        get() = CoroutineExceptionHandler
        
    override fun handleException(context: CoroutineContext, exception: Throwable) {
     val msg =  exception.stackTraceToString()
        Log.e("$errCode"."GlobalCoroutineExceptionHandler:${msg}")}}Copy the code

Then we use Kotlin’s extension function to simplify our use, eliminating the process of writing lifecycleScope. Launch and exceptionHandler repeatedly. We define three common methods.

inline fun AppCompatActivity.requestMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}inline fun AppCompatActivity.requestIO(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit): Job {
    return lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
       block.invoke(this)}}inline fun AppCompatActivity.delayMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false,
        delayTime: Long.noinline block: suspend CoroutineScope. () - >Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
         block.invoke(this)}}Copy the code

At this point we can happily use it in an Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        requestMain {
            delay(2000)
            Toast.makeText(this@MainActivity."haha",Toast.LENGTH_SHORT).show()
        }
        requestIO {
            loadNetData()
        }
        delayMain(100){
            Toast.makeText(this@MainActivity."haha",Toast.LENGTH_SHORT).show()
        }
    }
    
    private suspend fun loadNetData(a){
        // Network loading}}Copy the code

Similarly, let’s extend a set of fragment-based methods

inline fun Fragment.requestMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}inline fun Fragment.requestIO(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
    lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}inline fun Fragment.delayMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false, delayTime: Long.noinline block: suspend CoroutineScope. () - >Unit) {
    lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
        block.invoke(this)}}Copy the code

It can then be used happily in fragments as well

class HomeFragment:Fragment() {

    init {
        lifecycleScope.launchWhenCreated {
            Toast.makeText(context,"Fragment created", Toast.LENGTH_SHORT).show()
        }
    }
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup? , savedInstanceState:Bundle?).: View? {
        return inflater.inflate(R.layout.fragment_main,container,false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?). {
        super.onViewCreated(view, savedInstanceState)
        requestMain {
            / /...
        }
        requestIO {
            / /...
        }
        delayMain(100) {/ /...}}}Copy the code

It needs to be mentioned here that some people may not understand why we write Activity and Fragment separately. They are both using lifecycleScope, so we can not extend them directly through lifecycleScope. Suppose we extend it this way:

inline fun LifecycleCoroutineScope.requestMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
        launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}Copy the code

Let’s use Dailog as an example to start a coroutine:

val dialog = Dialog(this)
dialog.show()
(dialog.context as LifecycleOwner).lifecycleScope.requestMain { 
    withContext(Dispatchers.IO){
        // Network loading
    }
    / / refresh the UI
}
dialog.cancel()
Copy the code

So what could possibly go wrong? Yes, memory leaks and misreferences. Although my dialog is DESTROYED, our lifecycleScope is not in a DESTROYED state, so our coroutine will still be executed and we will have memory leaks and crashes.

Through the above learning, we have basically mastered how to use coroutines in activities and fragments. Next we’ll look at using coroutines in the Viewmodel.

Coroutines are used in the ViewModel

If we want to use coroutines in viewModels as easily and quickly as we do in activities and fragments. Then we need to integrate the following official ViewModel extension library.

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

Instead of using lifecycleScope for activities and fragments, we use viewModelScope, viewModelScope, viewModelScope. It’s so important that it should be repeated for three times.

A few people have asked me why I can’t use coroutines in viewModel, and I started to wonder why I can’t use coroutines in viewModel. The final result is using lifecycleScope in the ViewModel, which is not the right thing to do.

public val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope: CoroutineScope? = this.getTag(JOB_KEY)
        if(scope ! =null) {
            return scope
        }
        return setTagIfAbsent(
            JOB_KEY,
            CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
        )
    }

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close(a) {
        coroutineContext.cancel()
    }
}
Copy the code

ViewModelScope is a little simpler than the lifecycleScope implementation. The container is designed to be used in the same context as the lifecycleScope, except that the viewModelScope is canceled when the ViewModel is destroyed.

final void clear(a) {
    mCleared = true;
    if(mBagOfTags ! =null) {
        synchronized (mBagOfTags) {
            for (Object value : mBagOfTags.values()) {
                closeWithRuntimeException(value);
            }
        }
    }
    onCleared();
}


<T> T setTagIfAbsent(String key, T newValue) {
    T previous;
    synchronized (mBagOfTags) {
        previous = (T) mBagOfTags.get(key);
        if (previous == null) {
            mBagOfTags.put(key, newValue);
        }
    }
    T result = previous == null ? newValue : previous;
    if (mCleared) {
        closeWithRuntimeException(result);
    }
    return result;
}

private static void closeWithRuntimeException(Object obj) {
    if (obj instanceof Closeable) {
        try {
            ((Closeable) obj).close();
        } catch (IOException e) {
            throw newRuntimeException(e); }}}Copy the code

Similarly, with the above summary, we extend a set of common methods for ViewModel

inline fun ViewModel.requestMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
    viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}inline fun ViewModel.requestIO(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false.noinline block: suspend CoroutineScope. () - >Unit) {
    viewModelScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        block.invoke(this)}}inline fun ViewModel.delayMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false, delayTime: Long.noinline block: suspend CoroutineScope. () - >Unit) {
    viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
        withContext(Dispatchers.IO) {
            delay(delayTime)
        }
        block.invoke(this)}}Copy the code

Then we can have fun using coroutines in the ViewModel.

class MainViewModel:ViewModel() {

    init {
        requestMain {
            Log.d("MainViewModel"."Start coroutine in main thread")
        }
        requestIO {
            Log.d("MainViewModel"."IO thread starts coroutine for network loading")
        }
        delayMain(100){
            Log.d("MainViewModel"."Start the coroutine in the main thread and delay it for a certain time.")}}}Copy the code

Okay, so I’ve learned the normal way to use coroutines. But what do we do when we use lifecycleScope and viewModelScope in some environments? For example, how to use it in Service, Dialog, PopWindow, and other environments.

Use coroutines in other environments

In these environments, we can use a general way of processing, actually according to the coroutine scope difference is divided into two types:

  • Synergistic scope: We imitate this kindMainScopeCustom oneCoroutineScope.
  • Master/slave (supervisory) scope: We use this category directlyMainScope“, and then make some extensions on this basis.

If you don’t understand these two concepts, please go to Chapter 2 and read them carefully. There will be no explanation here.

We’re going to create a CoroutineScope that mimics MainScope, which is running on the main thread and whose Job is not the SupervisorJob.

@Suppress("FunctionName")
public fun NormalScope(a): CoroutineScope = CoroutineScope(Dispatchers.Main)
Copy the code

Then I used NormalScope and MainScope. Let’s take Service as an example.

abstract class BaseService :Service() {private val normalScope = NormalScope()

    override fun onDestroy(a) {
        normalScope.cancel()
        super.onDestroy()
    }

    protected fun requestMain(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope. () - >Unit) {
        normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            block.invoke(this)}}protected fun requestIO(
        errCode: Int = - 1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope. () - >Unit): Job {
        return normalScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            block.invoke(this)}}protected fun delayMain(
        delayTime: Long,errCode: Int = - 1, errMsg: String = "", report: Boolean = false,
        block: suspend CoroutineScope. () - >Unit) {
        normalScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {
            withContext(Dispatchers.IO) {
                delay(delayTime)
            }
            block.invoke(this)}}}Copy the code

Once we create an abstract BaseService class and define some basic usage methods, I can use it quickly

class MainService : BaseService() {

    override fun onBind(intent: Intent): IBinder?  = null

    override fun onStartCommand(intent: Intent? , flags:Int, startId: Int): Int {
        requestIO {
            // Network loading
        }
        return super.onStartCommand(intent, flags, startId)
    }
}
Copy the code

The same can be done in Dialog, PopWindow, and other environments to define a CoroutineScope that meets our own needs. Be sure not to use it across domains, and close coroutines in a timely manner.

At the end of this article, we’ve looked at the basic use of coroutines with activities, Fragments, Lifecycle, Viewmodel, and how to easily customize a coroutine. If you don’t know anything else, leave a comment below.

Need source code to see here: Demo source

trailer

In the next article we will use it in combination with Android Jetpack components such as DataBinding, LiveData,Room, etc.

Originality is not easy. If you like this article, you can click “like”.

Related articles

  • Basic usage of Kotlin coroutines
  • Kotlin coroutine introduction to Android version in detail (ii) -> Kotlin coroutine key knowledge points preliminary explanation
  • Kotlin coroutine exception handling
  • Use Kotlin coroutine to develop Android applications
  • Network request encapsulation for Kotlin coroutines

Extend the series

  • Encapsulating DataBinding saves you thousands of lines of code
  • A ViewModel wrapper for everyday use