教程 - 使用 IntelliJ IDEA 调试 Kotlin 数据流(Flow)
本教程演示如何创建 Kotlin 数据流(Flow), 并使用 IntelliJ IDEA 调试它.
本教程假定你已经具备了 协程 和 Kotlin 数据流(Flow) 的相关知识.
创建 Kotlin 数据流
创建一个 Kotlin 数据流(Flow), 其中包括一个慢速的发射者(emitter)和一个慢速的收取者(collector):
在 IntelliJ IDEA 中打开一个 Kotlin 项目. 如果你没有项目, 请 创建项目.
要在 Gradle 项目中使用
kotlinx.coroutines
库, 请向build.gradle(.kts)
添加以下依赖项:dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") }dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3' }对于其他构建系统, 请参见
kotlinx.coroutines
README 中的说明.打开
src/main/kotlin
中的Main.kt
文件 .src
目录包含 Kotlin 源代码文件和资源.Main.kt
文件包含一段输出Hello World!
的示例代码.创建
simple()
函数, 它返回一个数据流, 其中包含 3 个数字:import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlin.system.* fun simple(): Flow<Int> = flow { for (i in 1..3) { delay(100) emit(i) } }修改
main()
函数中的代码:使用
runBlocking()
代码块封装一个协程.使用
collect()
函数收取发射的值.使用
delay()
函数来模拟一段大量消耗 CPU 的阻塞代码. 这个函数会挂起协程 300 ms, 不会阻塞线程.使用
println()
函数打印从数据流收取的值.
fun main() = runBlocking { simple() .collect { value -> delay(300) println(value) } }点击 Build Project 构建代码.
调试协程
在调用
emit()
函数的代码行设置一个断点:点击屏幕顶部运行配置旁边的 Debug, 使用调试模式运行代码.
这时会出现 Debug 工具窗口:
Frames 页包含调用栈信息.
Variables 页包含当前上下文环境中的变量. 它告诉我们数据流正在发射第 1 个值.
Coroutines 页包含正在运行中的或者被挂起的协程信息.
点击 Debug 工具窗口中的 Resume Program, 回复调试器运行. 程序会在同一个断点再次暂停.
现在数据流发射第 2 个值.
被优化的变量
如果你使用 suspend
函数, 那么在调试器中, 你可能会在变量名称旁边看到 "was optimized out" 文字:
这段文字的意思是说, 变量的生存时间变短了, 而且变量已经不再存在了. 如果变量被优化, 调试代码会变得困难, 因为你看不到变量值. 你可以使用 -Xdebug
编译器选项禁止这种优化.
添加一个并发运行的协程
打开
src/main/kotlin
中的main.kt
文件.修改代码, 让发射者和收取者并发运行:
推荐一个
buffer()
函数调用, 并发运行发射者和收取者.buffer()
存储已发射的值, 并在一个独立的协程中运行数据流收取者.
fun main() = runBlocking<Unit> { simple() .buffer() .collect { value -> delay(300) println(value) } }点击 Build Project, 构建代码.
调试有 2 个协程的 Kotlin 数据流
在
println(value)
处设置一个新断点.点击屏幕顶部运行配置旁边的 Debug, 使用调试模式运行代码.
这时会出现 Debug 工具窗口.
在 Coroutines 页中, 你可以看到存在 2 个协程并发运行. 由于调用了
buffer()
函数, 数据流收取者和发射者运行在独立的协程中.buffer()
函数会缓存数据流中发射的值. 发射者协程状态为 RUNNING, 而收取者协程状态为 SUSPENDED.点击 Debug 工具窗口中的 Resume Program, 回复调试器运行.
现在收取者协程状态为 RUNNING, 而发射者协程状态为 SUSPENDED.
你可以深入挖掘各个协程的信息, 来调试你的代码.