A Lifecycle.

Currently, AAC(Android Architecture Components) is part of Android Jetpack. Lifecycle is a component of AAC that can manage the Lifecycle of activities and fragments.

Lifecycle can build life-cycle-aware components that automatically adjust their behavior based on the current life-cycle state of activities and fragments. Also, LiveData and ViewModel lifecycle also depend on Lifecycle.

Create the LifecycleObserver implementation class

First, create a LifecycleObserver LifecycleCoroutineListener interface implementation class, On an Activity/Fragment Lifecycle Event (Lifecycle.event.on_destroy by default), the coroutine calls the canceled method.

open class LifecycleCoroutineListener(private val job: Job,
                                 private val cancelEvent: Lifecycle.Event = Lifecycle.Event.ON_DESTROY) : LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun pause(a) = handleEvent(Lifecycle.Event.ON_PAUSE)

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop(a) = handleEvent(Lifecycle.Event.ON_STOP)

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy(a) = handleEvent(Lifecycle.Event.ON_DESTROY)

    private fun handleEvent(e: Lifecycle.Event) {

        if(e == cancelEvent && ! job.isCancelled) { job.cancel() } } }Copy the code

3. List the application scenarios

3.1 Use coroutines and bind life cycles

Create GlobalScope’s extension asyncWithLifecycle, which uses Async to create a Deferred object, And use LifecycleCoroutineListener lifecycleOwner lifecycle to binding.

fun <T> GlobalScope.asyncWithLifecycle(lifecycleOwner: LifecycleOwner,
                                       context: CoroutineContext = EmptyCoroutineContext,
                                       start: CoroutineStart = CoroutineStart.DEFAULT,
                                       block: suspend CoroutineScope. () -> T): Deferred<T> {

    val deferred = GlobalScope.async(context, start) {

        block()
    }

    lifecycleOwner.lifecycle.addObserver(LifecycleCoroutineListener(deferred))

    return deferred
}
Copy the code

If you need to display toast, you must use dispatchers. Main to display toast in the Main thread:

        GlobalScope.asyncWithLifecycle(this,Dispatchers.Main) {

            delay(1000)

            Toast.makeText(mContext,"hi, this must use 'Dispatchers.Main'",Toast.LENGTH_SHORT).show()
        }
Copy the code

When using dispatchers. Main, you need to add it in build.gradle

implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.0.1'
Copy the code

3.2 Block binding life cycle of coroutines

Create a GlobalScope extension function, bindWithLifecycle, whose coroutine block binds the lifecycle when called.

fun <T> GlobalScope.bindWithLifecycle(lifecycleOwner: LifecycleOwner,
                                      block: CoroutineScope. () -> Deferred<T>): Deferred<T> {

    val deferred = block.invoke(this)

    lifecycleOwner.lifecycle.addObserver(LifecycleCoroutineListener(deferred))

    return deferred
}
Copy the code

You can use it in the following ways:

        GlobalScope.bindWithLifecycle(this) {

            GlobalScope.async(Dispatchers.Main) {

                val deferred1 = async(Dispatchers.Default) {

                    delay(1000)
                    1
                }

                val deferred2 = async(Dispatchers.Default) {

                    delay(1500)
                    2
                }

                val result = deferred1.await() + deferred2.await()

                Toast.makeText(mContext,"the result is $result",Toast.LENGTH_SHORT).show()
            }
        }
Copy the code

3.3 then

Create the extension function then of Deferred, which creates a coroutine to run on the UI thread:

infix fun <T> Deferred<T>.then(block: (T) -> Unit): Job {

    return GlobalScope.launch(context = Dispatchers.Main) {

        block(this@then.await())
    }
}
Copy the code

Since it uses Infix and is used with asyncWithLifecycle:

        GlobalScope.asyncWithLifecycle(this,Dispatchers.IO) {

            delay(5000) // Simulate a time-consuming network request
            1
        } then {

            Toast.makeText(mContext,"the result is $it",Toast.LENGTH_SHORT).show()
        }
Copy the code

AsyncWithLifecycle uses dispatchers. IO to simulate time-consuming network requests. This can also be used in real development, where the results of network requests are presented using THEN.

3.4 thenAsync

ThenAsync is similar to then, except that the object returned is different.

infix fun <T, R> Deferred<T>.thenAsync(block: (T) -> R): Deferred<R> {

    return GlobalScope.async(context = Dispatchers.Main) {

        block(this@thenAsync.await())
    }
}
Copy the code

ThenAsync returns a Deferred object, so you can use the following chain call:

        GlobalScope.asyncWithLifecycle(this, Dispatchers.IO) {

            delay(5000) // Simulate a time-consuming network request
            1
        } thenAsync {

            it + 2
        } then {

            Toast.makeText(mContext,"the result is $it", Toast.LENGTH_SHORT).show()
        }
Copy the code

3.5 awaitOrNull

Create an extension of the Deferred function, awaitOrNull, which references the Timeout method of Java futures. If timeout or exception is encountered, null is returned.

suspend fun <T> Deferred<T>.awaitOrNull(timeout: Long = 0L): T? {
    return try {
        if (timeout > 0) {

            withTimeout(timeout) {

                this@awaitOrNull.await()
            }

        } else {

            this.await()
        }
    } catch (e: Exception) {

        Log.e("Deferred", e.message)
        null}}Copy the code

In the following example, deferred takes 5 seconds to return, but deferred uses awaitOrNull(), which has a timeout of 4 seconds, so result is null.

        val deferred = GlobalScope.asyncWithLifecycle(this, Dispatchers.IO) {

            delay(5000) // Simulate a time-consuming network request
            1
        }

        GlobalScope.asyncWithLifecycle(this,Dispatchers.Main) {

            val result = deferred.awaitOrNull(4000)

            Toast.makeText(mContext,"the result is $result", Toast.LENGTH_SHORT).show()
        }
Copy the code

If the timeout is set to more than 5 seconds, result returns the correct value.

3.6 Binding any Job to the life cycle

Remember the original LifecycleCoroutineListener? It uses Open, so any coroutine you create can use it to bind the life cycle.

4. To summarize

This article explains some of the Android use scenarios for coroutines.

However, this article is just a primer. Kotlin’s coroutine is now a formal version that can be considered for production.

Finally, attach the github address of the library: github.com/fengzhizi71…


Java and Android technology stack: update and push original technical articles every week, welcome to scan the qr code of the public account below and pay attention to, looking forward to growing and progress with you together.