抽丝剥茧聊Kotlin协程之协程避坑指南

简介: 抽丝剥茧聊Kotlin协程之协程避坑指南

640.jpg



1.前言


如果要我拿现实中的一事物与Kotlin协程中的Job做一个类比,那么我会把Job比作成海洋中的冰山。自由漂浮的冰山约有90%体积沉在海水表面下,因此看着浮在水面上的形状并猜不出水下的形状。与冰山一样,Job提供给开发者的功能非常简单,但是在协程框架内部Job却做了大量的工作。至关重要的是,如果开发者不去深入了解Job内部的实现机制,那么在使用协程的过程中,他就犹如开着船与冰山擦肩而过的船长,随时有可能面临系统崩溃的风险。如果你的项目中正在使用协程,如果你想享受协程编程带来的便利的同时又想保证程序的健壮性。那么深入理解Job的内部机制,会让你在遇到协程问题时,更从容,更游刃有余。


为了检验各位读者对协程Job的了解程度,我设计了两段代码,请问哪个Case “CancelJobActivity job2 finished”语句会被打印出来?


程序一:


private fun case1() {
    val scope = MainScope()
    scope.launch(Job()) {
        launch {
            delay(2000L)
            println("CancelJobActivity job1 finished")
            scope.cancel()
        }
        launch {
            delay(3000L)
            println("CancelJobActivity job2 finished")
        }
    }
}


程序二:


private fun case2() {
    val scope = MainScope()
    scope.launch {
        launch {
            delay(2000L)
            println("CancelJobActivity job1 finished")
            scope.cancel()
        }
        launch {
            delay(3000L)
            println("CancelJobActivity job2 finished")
        }
    }
}


上述两段代码的功能是:通过MainScope启动一个协程,在该协程中启动两个子协程。子协程1在delay 2s后打印语句并取消MainScope启动的协程,子协程2在delay 3s后打印语句。我们的期望是,子协程1在调用scope.cancel方法后,子协程2不会输出语句。程序二如我们所愿,但是程序一在3s后仍然输出了。显然程序一的结果不被我们接受。它们唯一的区别就是在scope.launch处是否有Job()参数。


为什么launch()方法增加Job()参数,就无法取消掉协程呢?要搞懂这个问题,就必须先搞懂,父子协程中的数据结构。引用Google官方公开的图片,我们可以看到,这是典型的树数据结构。

640.png

2. 原因分析


  1. 根据MainScope源码,我们知道scope对应的Job类型是SupervisorJob。
  2. launch方法创建的协程本身也是Job类型。而且它与启动该协程的CoroutineContext[Job]形成父子关系。
  3. launch(Job())根据CoroutineContext的plus方法,会用新的newJob代替SupervisorJob。


public fun MainScope(): CoroutineScope 
= ContextScope(SupervisorJob() + Dispatchers.Main)
  1. CoroutineScope.cancel方法,正是coroutineContext[Job]对应的Job cancel的。
public fun CoroutineScope.cancel(cause: CancellationException? = null) {
    val job = coroutineContext[Job] ?: error("Scope cannot be cancelled because it does not have a job: $this")
    job.cancel(cause)
}


程序二的关系图


640.png

640.png


程序一的Job关系图

640.png

640.png



从Job关系图,我们看出MainScope对应的SupervisorJob在程序一中,单独游离出来,并未与Job0、Job1、Job2形成有效的树形结构关系,所以通过scope.cancel()无法取消。


最后 Job相关的文章我构思了很久,由于Job比较复杂,一时间竟无从下手。本文权当一个起笔,Job原理与源码相结合的文章,后续会陆续更新。本文中的案例仅仅是Job难题中的冰山一角,cancel和异常处理机制,与Job树形结构关系图可以变化出很多种情况,但是如果能真正理解原理,处理起来也并不麻烦。下次我会从源码的角度,讲解Job的树形结构关系是如何建立起来的,以及Job的cancel机制是如何双向传播和异常处理机制是如何向上传播的。

相关文章
|
2月前
|
调度 开发者 UED
Kotlin 中的协程是什么?
【8月更文挑战第31天】
56 0
|
4月前
|
存储 Java 调度
Android面试题之Kotlin 协程的挂起、执行和恢复过程
了解Kotlin协程的挂起、执行和恢复机制。挂起时,状态和上下文(局部变量、调用栈、调度器等)被保存;挂起点通过`Continuation`对象处理,释放线程控制权。当恢复条件满足,调度器重新分配线程,调用`resumeWith`恢复执行。关注公众号“AntDream”获取更多并发知识。
79 2
|
5月前
|
移动开发 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美融合
【5月更文挑战第25天】 在移动开发的世界中,性能和响应性是衡量应用质量的关键指标。随着Kotlin的流行和协程的引入,Android开发者现在有了更强大的工具来提升应用的性能和用户体验。本文深入探讨了Kotlin语言如何与协程相结合,为Android应用开发带来异步处理能力的同时,保持代码的简洁性和可读性。我们将通过实际案例分析,展示如何在Android项目中实现协程,以及它们如何帮助开发者更有效地管理后台任务和用户界面的流畅交互。
|
5月前
|
移动开发 监控 Android开发
构建高效安卓应用:Kotlin 协程的实践与优化
【5月更文挑战第16天】 在移动开发领域,性能优化一直是开发者们追求的重要目标。特别是对于安卓平台来说,由于设备多样性和系统资源的限制,如何提升应用的响应性和流畅度成为了一个关键议题。近年来,Kotlin 语言因其简洁、安全和高效的特点,在安卓开发中得到了广泛的应用。其中,Kotlin 协程作为一种轻量级的并发解决方案,为异步编程提供了强大支持,成为提升安卓应用性能的有效手段。本文将深入探讨 Kotlin 协程在安卓开发中的应用实践,以及通过合理设计和使用协程来优化应用性能的策略。
49 8
|
5月前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin的协程优势
【5月更文挑战第22天】随着移动开发技术的不断进步,Android平台的性能优化已经成为开发者关注的焦点。在众多提升应用性能的手段中,Kotlin语言提供的协程概念因其轻量级线程管理和异步编程能力而受到广泛关注。本文将深入探讨Kotlin协程在Android开发中的应用,以及它如何帮助开发者构建出更高效、响应更快的应用,同时保持代码的简洁性和可读性。
|
5月前
|
移动开发 Android开发 开发者
构建高效安卓应用:Kotlin 协程的实践指南
【5月更文挑战第18天】 随着移动开发技术的不断进步,安卓平台亟需一种高效的异步编程解决方案来应对日益复杂的应用需求。Kotlin 协程作为一种新兴的轻量级线程管理机制,以其简洁的语法和强大的功能,成为解决这一问题的关键。本文将深入探讨Kotlin协程在安卓开发中的实际应用,从基本概念到高级技巧,为开发者提供一份全面的实践指南,旨在帮助读者构建更加高效、稳定的安卓应用。
|
5月前
|
移动开发 安全 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【5月更文挑战第17天】 在移动开发领域,性能优化和流畅的用户体验是关键。对于Android平台而言,Kotlin语言凭借其简洁性和功能安全性成为开发的首选。与此同时,协程作为一种新的并发处理方式,在简化异步编程方面展现出巨大潜力。本文将深入探讨如何通过Kotlin语言以及协程技术,提升Android应用的性能和响应能力,并确保用户界面的流畅性。
|
5月前
|
移动开发 数据库 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【5月更文挑战第7天】 在移动开发领域,性能优化和资源管理始终是核心议题。随着Kotlin语言的普及,其提供的协程特性为Android开发者带来了异步编程的新范式。本文将深入探讨如何通过Kotlin协程来优化Android应用的性能,实现流畅的用户体验,并减少资源消耗。我们将分析协程的核心概念,并通过实际案例演示其在Android开发中的应用场景和优势。
|
5月前
|
移动开发 数据处理 Android开发
构建高效Android应用:Kotlin的协程与Flow的使用
【5月更文挑战第23天】 在移动开发领域,性能优化和异步编程一直是核心议题。随着Kotlin语言在Android开发中的普及,其提供的协程(coroutines)和流式编程(Flow)功能为开发者带来了革命性的工具,以更简洁、高效的方式处理异步任务和数据流。本文将深入探讨Kotlin协程和Flow在Android应用中的实际应用,以及它们如何帮助开发者编写更加响应迅速且不阻塞用户界面的应用程序。我们将通过具体案例分析这两种技术的优势,并展示如何在现有项目中实现这些功能。
|
5月前
|
算法 安全 Android开发
深入理解操作系统的内存管理机制构建高效Android应用:Kotlin的协程优势
【4月更文挑战第30天】 在现代计算机系统中,操作系统的内存管理是确保系统高效、稳定运行的关键。本文将探讨操作系统内存管理的核心技术,包括内存分配、虚拟内存、分页和分段等概念,以及它们是如何协同工作以提高内存利用率和系统性能的。通过对这些技术的详细分析,我们可以更好地理解操作系统背后的原理,并评估不同内存管理策略对系统行为的影响。 【4月更文挑战第30天】 在移动开发领域,尤其是针对Android平台,性能优化和流畅的用户体验始终是开发者追求的核心目标。随着Kotlin语言的普及,协程作为其在异步编程领域的杀手锏特性,已经逐渐成为提高应用性能和简化代码结构的重要工具。本文将深入探讨Kotli