AAC 的 Lifecycle 结合 Kotlin Coroutines 进行使用

简介: AAC 的 Lifecycle 结合 Kotlin Coroutines 进行使用

一. Lifecycle



目前,AAC(Android Architecture Components简称)  已经是 Android Jetpack 的一部分。Lifecycle 是 AAC 其中的一个组件,Lifecycle 能够管理 Activity 和 Fragment 的生命周期。


Lifecycle 可以构建感知生命周期的组件 —— 这些组件根据 Activity、Fragment 的当前生命周期状态自动调整其行为。并且,LiveData 与 ViewModel 的 lifecycle 也依赖于 Lifecycle。


二. 创建 LifecycleObserver 的实现类



首先,创建一个 LifecycleObserver 接口的实现类 LifecycleCoroutineListener,在 Activity/Fragment 某个生命周期事件上(默认为Lifecycle.Event.ON_DESTROY),协程会调用取消的方法。

open class LifecycleCoroutineListener(private val job: Job,
                                 private val cancelEvent: Lifecycle.Event = Lifecycle.Event.ON_DESTROY) : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun pause() = handleEvent(Lifecycle.Event.ON_PAUSE)
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun stop() = handleEvent(Lifecycle.Event.ON_STOP)
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() = handleEvent(Lifecycle.Event.ON_DESTROY)
    private fun handleEvent(e: Lifecycle.Event) {
        if (e == cancelEvent && !job.isCancelled) {
            job.cancel()
        }
    }
}


三. 列举使用场景



3.1 使用协程并绑定生命周期


创建 GlobalScope 的扩展函数 asyncWithLifecycle,它使用async创建一个 deferred 对象,并使用 lifecycleOwner 的 lifecycle 去绑定 LifecycleCoroutineListener。

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
}


可以采用如下的方式进行使用,如果需要显示toast的话,必须使用 Dispatchers.Main 这样才能在主线程显示 toast:

GlobalScope.asyncWithLifecycle(this,Dispatchers.Main) {
            delay(1000)
            Toast.makeText(mContext,"hi, this must use 'Dispatchers.Main'",Toast.LENGTH_SHORT).show()
        }


使用 Dispatchers.Main 时,需要在 build.gradle 中添加

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'


3.2 协程的block绑定生命周期


创建 GlobalScope 的扩展函数 bindWithLifecycle,它的协程 block 在调用时绑定生命周期。

fun <T> GlobalScope.bindWithLifecycle(lifecycleOwner: LifecycleOwner,
                                      block: CoroutineScope.() -> Deferred<T>): Deferred<T> {
    val deferred = block.invoke(this)
    lifecycleOwner.lifecycle.addObserver(LifecycleCoroutineListener(deferred))
    return deferred
}


可以采用如下的方式进行使用:

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()
            }
        }


3.3 then


创建 Deferred 的扩展函数 then,它会创建一个协程并运行在 UI 线程上:

infix fun <T> Deferred<T>.then(block: (T) -> Unit): Job {
    return GlobalScope.launch(context = Dispatchers.Main) {
        block(this@then.await())
    }
}


由于它使用了infix,并结合 asyncWithLifecycle 一起使用:

GlobalScope.asyncWithLifecycle(this,Dispatchers.IO) {
            delay(5000) // 模拟耗时的网络请求
            1
        } then {
            Toast.makeText(mContext,"the result is $it",Toast.LENGTH_SHORT).show()
        }


asyncWithLifecycle 使用了Dispatchers.IO来模拟一些耗时的网络请求。在实际开发中也可以这样使用,网络请求的结果使用then进行展示。


3.4 thenAsync


thenAsync 类似于 then,区别在于返回的对象不同。

infix fun <T, R> Deferred<T>.thenAsync(block: (T) -> R): Deferred<R> {
    return GlobalScope.async(context = Dispatchers.Main) {
        block(this@thenAsync.await())
    }
}


thenAsync 返回的是 Deferred 对象,因此可以使用如下的链式调用:

GlobalScope.asyncWithLifecycle(this, Dispatchers.IO) {
            delay(5000) // 模拟耗时的网络请求
            1
        } thenAsync {
            it + 2
        } then {
            Toast.makeText(mContext,"the result is $it", Toast.LENGTH_SHORT).show()
        }


3.5 awaitOrNull


创建 Deferred 的扩展函数 awaitOrNull,它参考了 Java 的 Future 的超时方法。如果遇到超时或者异常,则返回null。

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
    }
}


下面的例子中,deferred 会在 5秒才有返回值,但是deferred 使用了awaitOrNull(),它的超时时间是4秒,因此 result 的值为 null。

val deferred = GlobalScope.asyncWithLifecycle(this, Dispatchers.IO) {
            delay(5000) // 模拟耗时的网络请求
            1
        }
        GlobalScope.asyncWithLifecycle(this,Dispatchers.Main) {
            val result = deferred.awaitOrNull(4000)
            Toast.makeText(mContext,"the result is $result", Toast.LENGTH_SHORT).show()
        }


如果把超时时间设置大于5秒的话,result 会返回正确的值。


3.6 任意 job 绑定到生命周期


还记得最初的 LifecycleCoroutineListener 嘛?它使用了open,因此任意创建的协程都可以使用它来绑定生命周期。


四. 总结



本文讲解了协程在 Android 的一些使用场景。


然而,本文只是抛砖引玉,目前 Kotlin 的协程已经是正式版本,可以考虑引入生产环境。

相关文章
|
安全 调度 数据库
[译] 关于 Kotlin Coroutines, 你可能会犯的 7 个错误
[译] 关于 Kotlin Coroutines, 你可能会犯的 7 个错误
[译] 关于 Kotlin Coroutines, 你可能会犯的 7 个错误
|
开发者 C++ Kotlin
Kotlin Coroutines Flow 系列(五) 其他的操作符
Kotlin Coroutines Flow 系列(五) 其他的操作符
171 0
|
Java 调度 C++
Kotlin Coroutines Flow 系列(四) 线程操作
Kotlin Coroutines Flow 系列(四) 线程操作
216 0
|
Android开发 C++ Kotlin
Kotlin Coroutines Flow 系列(三) 异常处理
Kotlin Coroutines Flow 系列(三) 异常处理
482 0
|
缓存 C++ Kotlin
Kotlin Coroutines Flow 系列(二) Flow VS RxJava2
Kotlin Coroutines Flow 系列(二) Flow VS RxJava2
170 0
Kotlin Coroutines Flow 系列(二) Flow VS RxJava2
|
Java API Kotlin
Kotlin Coroutines Flow 系列(一) Flow 基本使用
Kotlin Coroutines Flow 系列(一) Flow 基本使用
270 0
Kotlin Coroutines Flow 系列(一) Flow 基本使用
|
Android开发 Kotlin
Android 开发中 Kotlin Coroutines 如何优雅地处理异常
Android 开发中 Kotlin Coroutines 如何优雅地处理异常
358 0
|
安全 Java 调度
Kotlin Coroutines 笔记 (二)
Kotlin Coroutines 笔记 (二)
152 0
Kotlin Coroutines 笔记 (二)
|
编译器 API 调度
Kotlin Coroutines 笔记 (一)
Kotlin Coroutines 笔记 (一)
131 0
Kotlin Coroutines 笔记 (一)
|
Java
Kotlin协程简介(一) Hello,coroutines!
## 协程的作用 协程并不是一个新鲜概念,相信大家都有所了解,它的好处是可以极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能。它和反应式编程一样都可以有效的提高资源的利用率,并且让我们脱离`callback hell`。目前JAVA里还没有原生的协程库(AJDK里对协程提供了支持)。Kotlin从1.1开始支持协程,不过目前还处于试验阶段,感兴趣的同学可以查看[
5926 0