日常知识通
柔彩主题三 · 更轻盈的阅读体验

Kotlin协程封装:让异步代码更清爽

发布时间:2025-12-12 13:32:31 阅读:372 次

为什么需要封装协程?

写过Android或者后端Kotlin项目的人应该都有体会,刚开始用协程时觉得特别香,不用再回调地狱了。可时间一长,问题就来了:每个网络请求都要写一遍launch、try-catch、主线切换,重复代码堆得跟面条一样。

比如你做个登录功能,要调接口、转主线更新UI、处理异常弹Toast,三个地方都这么写一遍,到第六个页面你自己都想删代码了。

一个常见的“裸奔”写法

viewModelScope.launch {
    try {
        val result = repository.login(username, password)
        withContext(Dispatchers.Main) {
            // 更新UI
            showSuccess(result)
        }
    } catch (e: Exception) {
        withContext(Dispatchers.Main) {
            showToast(e.message)
        }
    }
}

这种写法在项目里复制粘贴几次,维护起来就很头疼。一旦需求改了,比如要统一加埋点或日志,就得一个个翻文件去改。

封装成一个扩展函数

我们可以把通用逻辑抽出来,变成一个扩展函数。比如叫 launchWithCatch:

inline fun CoroutineScope.launchWithCatch(
    crossinline onError: (String) -> Unit = {},
    noinline block: suspend CoroutineScope.() -> Unit
) = launch {
    try {
        block()
    } catch (e: Exception) {
        withContext(Dispatchers.Main) {
            onError(e.message ?: "未知错误")
        }
    }
}

然后调用的地方就干净多了:

launchWithCatch { toast ->
    showToast(toast)
} {
    val result = repository.login(username, password)
    withContext(Dispatchers.Main) {
        showSuccess(result)
    }
}

这时候你会发现,业务代码一眼就能看懂,出错处理也集中管理了。如果哪天产品说“所有错误都打个日志”,你只需要改这一个函数就行。

配合LiveData或StateFlow更顺手

很多项目用LiveData展示数据状态,比如加载中、成功、失败。可以再封装一个自动切换主线并发送状态的版本:

fun <T> LiveData<Result<T>>.launchFlow(
    block: suspend () -> T
) = liveDataScope.launch {
    emit(Result.Loading)
    try {
        val data = block()
        emit(Result.Success(data))
    } catch (e: Exception) {
        emit(Result.Error(e.message))
    }
}

调用时直接绑定到UI状态:

loginResult.launchFlow {
    repository.login(username, password)
}

XML里用observe监听 loginResult,自动处理Loading和Error状态,Activity/Fragment几乎不用写逻辑。

实际场景:电商App的商品详情页

打开商品页要干一堆事:拉商品信息、查库存、推荐列表、用户评价。以前得嵌套回调或者写一堆job变量控制完成状态,现在用封装后的协程,可以这么写:

launchWithCatch { msg ->
    showError(msg)
} {
    val product = async { repo.fetchProduct(id) }
    val stock = async { repo.checkStock(id) }
    val reviews = async { repo.getReviews(id) }

    // 等全部返回
    updateUI(product.await(), stock.await(), reviews.await())
}

既保证了并发效率,又没丢失可读性。用户看到的是秒开的页面,而你写的代码也不再是“一次性胶布代码”。

好的封装不是为了炫技,而是让团队里新手也能写出结构一致、容错性强的代码。Kotlin协程本身已经很强大,再加一层合适的包装,日常开发才能真正省心。