为了账号安全,请及时绑定邮箱和手机立即绑定

AAC 的 Lifecycle 结合 Kotlin Coroutines 进行使用

标签:
Android Kotlin

一. 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 的协程已经是正式版本,可以考虑引入生产环境。

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
1.7万
获赞与收藏
594

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消