Kotlin Jetpack 实战 | “不为人知”的协程调试技
前言
协程(Coroutines),是个让人又爱又恨
的东西。代码写起来是真的爽,调试起来是真的乱。
本文将介绍 Kotlin 协程的调试技巧
,不会涉及太多协程实际内容。所以,不管你有没有协程的基础,都可以看下去,如果有遇到不懂的概念直接忽略即可,后面我会系统讲解。
这篇文章是为我们协程系列打基础的,后面进入我们的协程部分《图解协程》
,具体的写作计划大家可以到这里看看:《Kotlin Jetpack 实战:目录》,欢迎提建议。
2. 前期准备
- 将 Android Studio 版本升级到最新
- 将我们的 Demo 工程 clone 到本地,用 Android Studio 打开: github.com/chaxiu/Kotl…
- 切换到分支:
chapter_08_coroutine_debug
- 强烈建议各位小伙伴小伙伴跟着本文一起实战,实战才是本文的精髓
3. 协程 JVM 参数
协程,可以理解为轻量级的线程。协程跟线程的关系,有点像“线程与进程的关系”。
在我们写 Java 并发代码的时候,我们经常会用 Thread.currentThread().name
带打印出当前线程的名字,然后通过日志来查看运行效果。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + "index = " + index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
/*
pool-1-thread-1index = 0
pool-1-thread-3index = 2
pool-1-thread-2index = 1
pool-1-thread-2index = 3
pool-1-thread-3index = 4
*/
复制代码
对于 Kotlin 协程,我们也可以做类似的事情。具体做法是,添加 JVM 参数:-Dkotlinx.coroutines.debug
。具体做法如下:
- 1.点击 IDE 的
Edit Configurations
- 2.将
-Dkotlinx.coroutines.debug
填入VM options
中:
在加上这个参数以后, 我们通过 Thread.currentThread().name
,就会自动为我们输出当前协程的名字。
// 这段代码看不懂没关系,忽略即可
// 请直接看后面的输出信息和注释
fun main() {
runBlocking<Unit> {
fun log(msg: Any) {
println("${Thread.currentThread().name} msg=$msg")
}
log(1)
launch {
val a = 4
delay(300)
log(a)
}
launch {
val b = 3
delay(200)
log(b)
}
launch {
val c = 2
delay(100)
log(c)
}
}
}
/*
// 输出:
1个线程 4个协程
↓ ↓
main @coroutine#1 msg=1
main @coroutine#4 msg=2
main @coroutine#3 msg=3
main @coroutine#2 msg=4
*/
复制代码
小结
- 一个线程可以对应多个协程
- 协程将线程划分成更小的单元
- 协程跟线程的关系,有点像“线程与进程的关系”。
看到这里,也许会有小伙伴说:“我还是搞不懂协程到底是什么,输出一个日志,能说明什么问题呢?”。
确实,我在刚学习协程的时候,也有这种苦恼,协程太抽象了,比线程还抽象
。线程它起码有一个 Thread.java
的源码给你看,协程呢,没有,Kotlin 编译器将它底层细节都屏蔽了。
那么,有没有更直观的方式来调试协程呢?
有的,只是目前国内知道的人应该不多。
前几天我在 Kotlin 官方的博客 中了解到:Kotlin 官方在 1.4 版本中为协程调试增加了许多支持。虽然 Kotlin 1.4 现在还没正式发布,但是我们可以抢先体验它的 RC 版本(Release Candidate Version)。
4. Kotlin 1.4 协程调试
Kotlin 1.3 最让我惊喜的是 Flow
,而 Kotlin 1.4 最让我惊喜的则是“协程调试支持”
。
在 Kotlin 1.4 之前,我一直都是通过 JVM 参数来研究协程的,这种方式并不友好,有的时候为了理解协程的代码,我需要加很多无关的 log。
一起看看 Kotlin 1.4 的变化:
Kotlin 1.4 之前 | Kotlin 1.4 | |
---|---|---|
断点 | 断点经常不生效 | 稳定 |
单步调试 | 单步调试经常不生效 | 稳定 |
单独的协程调试窗口 | 不支持 | 支持 |
查看协程创建栈 | 不支持 | 支持 |
查看协程调用栈 | 不支持 | 支持 |
协程挂起状态 | 不支持 | 支持 |
协程内存信息 dump | 不支持 | 支持 |
虽然 Kotlin 1.4 尚未发布,但这并不影响我们提前探索使用 Kotlin 1.4,毕竟早用早享受
嘛。
4-1 升级 Kotlin 版本
首先,将 Kotlin 相关的库升级到最新的:1.4-RC
const val kotlinVersion = "1.4.0-rc"
const val coroutines = "1.3.8-1.4.0-rc"
复制代码
4-2 Kotlin EAP 渠道
接下来,我们需要升级 IDE 自带的 Kotlin 插件版本:
-
1.
首先进入:Configure Kotlin Plugin Updates
-
2.
然后,在接下来的页面中,在Updates channel
下拉框中选择:1.4.x
,点击 check(需要梯子),接着 IDE 会提示你有新的 Kotlin 版本,你点击:install
安装,然后重启即可。(如果你在看这篇文章的时候,1.4 正式版已经发布了的话,这一步就可以省略了。你只需要确保 IDE 是最新的即可。)
4-3 协程的断点设置
-
3.
接下来在 IDE 中找到我们的示例代码,打一个断点,然后右键点击
断点,在弹出框中按照如下设置:
4-4 协程调试窗口
-
4.
然后就可以开始调试了,等程序停留在断点的位置后,会是这样。到目前为止,都还没有看见我们想要的协程调试窗,这时候需要我们手动点击红色箭头的位置:
-
5.
在弹出的窗口中,我们需要勾选coroutines
:
4-5 协程的调试栈
-
6.
勾选以后,我们就立马能看到一个专属于协程的调试框:
在上面的调试框中,我们能看到我们的代码当前有三个协程,其中1
的位置,代表:corouine2, coroutine3 的状态是 Suspend
;2
的位置,代表corouine4 的状态是 Running
;3
的位置,是该协程的创建栈。
4-6 协程 Dump
-
7.
正如我前面所说的,协程跟线程很像,所以它们两者的调试框也是类似的。比如,你还可以Dump
当前协程的内存状态,方便分析。
由于我们使用的并非正式的版本,Dump 出来的信息还比较少,相信之后官方对协程调试
的支持会越来越好。
5. 总结
-
工欲善其事,必先利其器
。在正式学习协程之前,我们先学会协程调试技巧,这将对我们后面学习协程有极大的帮助。 - 协程,并不是什么洪水猛兽,你完全可以将它看作一种
更轻量级的线程
。这句话我以后会反复讲,有些东西看起来复杂,本质却极其简单,协程就是这样的。 - 本文使用的是
1.4-rc
版本,写博客做研究是没问题的,但在生产环境是不推荐使用的。
觉得有用的话,点个赞吧~~也欢迎在下方评论,提出你的见解。