Jetpack实践指南:lifecycle与协程的“猫腻”事

110 篇文章 12 订阅
9 篇文章 4 订阅

本篇文章主要是讲解如何使用lifecycle创建协程 、源码解析以及lifecycle在协程中的应用。

lifecycle创建协程

private fun test55() {
    //创建协程
    lifecycleScope.launch {

    }
}

如上很简单,如果想要和Activity的生命周期绑定,还有下面一系列方法供你使用:

private fun test55() {
    //创建协程
    lifecycleScope.launchWhenResumed {  }
    lifecycleScope.launchWhenCreated {  }
    lifecycleScope.launchWhenStarted {  }
}

只有当对应生命周期执行了,才会执行协程块中的代码。

lifecycleScope是什么

# LifecycleOwner.kt
val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope

通过调用链看到,它是lifecycle提供的一个扩展属性

val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            //1.
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            //2.
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            //3.
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                //4.
                newScope.register()
                return newScope
            }
        }
    }

1.mInternalScopeRef是lifecycle内部的一个属性:

AtomicReference<Object> mInternalScopeRef = new AtomicReference<>();

初次调用其get()方法肯定是一个空的,所以会走到2处。

2.创建一个LifecycleCoroutineScopeImpl对象,看下它是个啥:

再往下走:

可以看到这就是一个CoroutineScope的子类,所以我们这样就创建了一个协程作用域对象,并且指定:

  • job类型为SupervisorJob,这样子job间发生异常而不会互相影响,阻止向上传递异常;
  • 分发器类型为Dispatchers.Main.immediate,默认分发到主线程执行

在这里顺便说下Dispatchers.Main.immediateDispatchers.Main的区别:

首先这两个都是指定把协程块内容分发到主线程中执行,但是前者多了个immediate,这其实是一种优化手段,我们看下官方文档怎么说:

简单说,如果创建协程块的线程和要指定的调度线程都是主线程,使用immediate的就不需要额外使用分发器进行分发了,这算是一个优化小手段

3.将创建的这个协程作用域对象通过CAS写入lifecycle的mInternalScopeRef,这样当 lifecycleScope.launch在此获取协程作用域就不会进行重复创建了,直接从mInternalScopeRef获取即可。

综上所述, lifecycleScope就是个协程作用域对象,用来在特定job和主线程中执行协程块代码逻辑。

4.注册观察者,当界面销毁时取消所有协程的执行

LifecycleCoroutineScopeImpl本身就是观察者对象,所以看下register()就是注册观察者:

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

同时LifecycleCoroutineScopeImpl重写了onStateChanged()方法:

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

就是为了方便当界面销毁了移除观察者,并取消所有的子协程代码块的执行,避免内存的泄漏。

协程是如何绑定Activity的生命周期

这个问题很简单,大家应该都可以想到:协程借助于添加观察者LifecyclObserver的方式实现对Activity生命周期的监听,这里主要通过两个非常典型的源码例子进行分析:

  1. lifecycleScope.launchWhenXXX:可以指定在特定的生命周期执行协程协程代码,当不再该状态时就会暂停协程块的执行

  2. lifecycle.repeatOnLifecycle:可以指定在特定的生命周期执行协程协程代码,当不再该状态时就会取消协程块的执行

请大家一定记住这两者的区别,当我们使用MutableStateFlowMutableSharedFlow时,在协程作用域添加观察者时强烈推荐使用第二种方式,之后应该会写一篇文章进行分析的。

lifecycleScope.launchWhenXXX

我看先看下调用链分析,这里以launchWhenResumed举例:

public fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
    lifecycle.whenResumed(block)
}

往下走:

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) {
    val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
    //1.
    val dispatcher = PausingDispatcher()
    val controller =
        //2\. 重点关注这个对象
        LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
    try {
        withContext(dispatcher, block)
    } finally {
        controller.finish()
    }
}

所以所有的lifecycleScope.launchWhenXXX都会走到whenStateAtLeast()这个方法:

1.创建可以暂停的分发器,就是通过这个对象实现在指定生命周期执行协程代码块,超出该生命周期暂停协程代码块的执行

canRun()方法是重点关注的,当暂停协程块执行时,该方法就会返回true,重新进行分发。

2.LifecycleController实现Activity生命周期监听,并借助上面创建的分发器,实现协程代码块的暂停执行和恢复执行,深入该类源码查看:

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) {
            //1.
            dispatchQueue.pause()
        } else {
            //2.
            dispatchQueue.resume()
        }
    }
}

一目了然,如果当前生命周期状态小于传入的minState,就调用DispatchQueue.pause()暂停协程代码块执行,也就是挂起,大于等于指定的生命周期就调用resume()方法恢复执行。

综上所诉,该方法lifecycleScope.launchWhenXXX当小于指定生命周期状态时,是暂停协程的执行,而不是取消

lifecycle.repeatOnLifecycle

直接看下源码:

public suspend fun Lifecycle.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
) {

    coroutineScope {
        withContext(Dispatchers.Main.immediate) {
            //1.
            if (currentState === Lifecycle.State.DESTROYED) return@withContext

            var launchedJob: Job? = null

            var observer: LifecycleEventObserver? = null
            try {
                //2.
                suspendCancellableCoroutine<Unit> { cont ->
                    val startWorkEvent = Lifecycle.Event.upTo(state)
                    val cancelWorkEvent = Lifecycle.Event.downFrom(state)
                    observer = LifecycleEventObserver { _, event ->
                        //3.
                        if (event == startWorkEvent) {
                            launchedJob = this@coroutineScope.launch {
                                block()
                            }
                            return@LifecycleEventObserver
                        }
                        //4.
                        if (event == cancelWorkEvent) {
                            launchedJob?.cancel()
                        }
                        //5.
                        if (event == Lifecycle.Event.ON_DESTROY) {
                            cont.resume(Unit)
                        }
                    }
                    //6.
                    this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
                }
            } finally {
                //7.
                launchedJob?.cancel()
                observer?.let {
                    this@repeatOnLifecycle.removeObserver(it)
                }
            }
        }
    }
}

上面的代码是经过精简过的(也没啥可以精简的),我们来一步步进行分析:

  1. 当页面状态处于销毁DESTROYED状态,直接return。

  2. suspendCancellableCoroutine是用来捕捉传递过来的Continuation,这样我们就可以决定挂起的协程什么时候可以恢复执行,比如delay()的实现机制就是如此。

PS: 插一嘴,不管是suspendCancellableCoroutine还是coroutineScope底层方法都是通过suspendCoroutineUninterceptedOrReturn实现,但是由于这个方法不正确使用会对代码产生安全影响,比如栈溢出,所以官方提供了前面两个封装方法。

  1. 创建了一个LifecycleEventObserver对象,用来绑定界面生命周期,当达到指定执行的生命周期后,就创建一个协程执行我们传递过来的代码块.

  2. 当小于当前指定的生命周期状态,就直接取消协程执行(请注意,和上面的whenStateAtLeast区别)。

  3. 当界面销毁时,才将挂起的协程恢复执行,所以这里我们就实现了根据具体场景来决定什么时候恢复协程执行。

  4. 6处和7处就是注册和反注册观察者。

综上所诉,该方法lifecycle.repeatOnLifecycle当小于指定生命周期状态时,是取消协程的执行,而不是暂停

作者:长安皈故里
链接:https://juejin.cn/post/7127057225247359012

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

一、架构师筑基必备技能

1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……

在这里插入图片描述

二、Android百大框架源码解析

1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程

在这里插入图片描述

三、Android性能优化实战解析

  • 腾讯Bugly:对字符串匹配算法的一点理解
  • 爱奇艺:安卓APP崩溃捕获方案——xCrash
  • 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
  • 百度APP技术:Android H5首屏优化实践
  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
  • 携程:从智行 Android 项目看组件化架构实践
  • 网易新闻构建优化:如何让你的构建速度“势如闪电”?

在这里插入图片描述

四、高级kotlin强化实战

1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》

  • 从一个膜拜大神的 Demo 开始

  • Kotlin 写 Gradle 脚本是一种什么体验?

  • Kotlin 编程的三重境界

  • Kotlin 高阶函数

  • Kotlin 泛型

  • Kotlin 扩展

  • Kotlin 委托

  • 协程“不为人知”的调试技巧

  • 图解协程:suspend

在这里插入图片描述

五、Android高级UI开源框架进阶解密

1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
在这里插入图片描述

六、NDK模块开发

1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习

在这里插入图片描述

七、Flutter技术进阶

1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)

在这里插入图片描述

八、微信小程序开发

1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……

在这里插入图片描述

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,可扫描下方CSDN官方认证卡片免费领取↓↓↓

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值