Kotlin协程入门:让异步编程更简单

Kotlin协程入门:让异步编程更简单

你有没有写过这样的代码:从网络请求数据,等结果回来再更新界面,接着又去读本地文件,每一步都得套一层回调?时间一长,代码像洋葱一样层层嵌套,看得人头晕。在Android开发或者基于JVM的后台服务中,这类问题尤其常见。Kotlin协程就是来解决这个痛点的。

协程不是线程,也不是新发明的概念,但它让异步操作写起来像写同步代码一样直观。你可以用看似“顺序执行”的方式处理耗时任务,而不必陷入回调地狱。

协程是什么?

想象你在煮泡面:烧水的时候你不用干站着,可以顺便切点火腿、拿碗筷。程序里也一样,协程允许你在等待某个操作(比如网络请求)完成时,先去执行别的任务,等结果到了再回来继续。它比开启多个线程更轻量,启动一个协程的开销远小于创建一个线程。

在Kotlin中,协程是语言层面的支持,通过库的形式提供,核心是kotlinx.coroutines。

第一个协程例子

在你的项目中引入依赖:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'

然后写一段简单的代码:

import kotlinx.coroutines.*

fun main() {
    println("开始")
    runBlocking {
        launch {
            delay(1000L)
            println("协程打印:你好")
        }
        println("非阻塞语句")
    }
    println("结束")
}

输出结果是:

开始
非阻塞语句
协程打印:你好
结束

这里runBlocking会阻塞主线程直到内部所有协程完成,launch启动了一个新的协程,delay(1000L)模拟耗时操作,但它不会像Thread.sleep()那样阻塞整个线程。

用async获取返回值

有时候你需要拿到异步操作的结果。比如同时请求用户信息和订单列表,等两者都完成后再展示页面。这时候可以用async

runBlocking {
    val user = async { fetchUser() }
    val orders = async { fetchOrders() }
    
    println("用户:${user.await()},订单数:${orders.await().size}")
}

suspend fun fetchUser(): String {
    delay(800L)
    return "zhangsan"
}

suspend fun fetchOrders(): List<String> {
    delay(1200L)
    return listOf("订单1", "订单2")
}

suspend关键字标记的函数只能在协程中调用,它们可以在不阻塞线程的情况下挂起和恢复。这正是协程的核心机制。

Android中的实际应用

在Android里,你通常不能在主线程做网络请求。以前的做法是开子线程,完成后切回主线程更新UI。现在可以这样写:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 启动协程
        lifecycleScope.launch {
            try {
                val data = withContext(Dispatchers.IO) {
                    fetchDataFromNetwork()
                }
                textView.text = data
            } catch (e: Exception) {
                textView.text = "加载失败"
            }
        }
    }

    private suspend fun fetchDataFromNetwork(): String {
        delay(2000L)
        return "从网络获取的数据"
    }
}

这里lifecycleScope绑定了Activity的生命周期,协程会自动在页面销毁时取消,避免内存泄漏。withContext(Dispatchers.IO)切换到IO线程执行耗时操作,而赋值给TextView的操作则自动回到主线程。

代码清晰,逻辑连贯,没有来回切换线程的混乱感。

取消协程:别忘了收尾

协程可以被取消。比如用户打开页面还没加载完就退出了,正在执行的请求应该停止。调用job.cancel()就能中断协程,但前提是协程能响应取消信号。

使用ensureActive()或定期调用可挂起函数(如yield()delay())都能触发检查。如果协程已经被取消,就会抛出CancellationException

协程不是万能药,但它是现代Kotlin开发中处理异步任务的首选方式。上手门槛低,效果立竿见影。试试把旧项目里的回调换成协程,你会发现代码一下子清爽了。