Kotlin协程中缀Builder模式实现:不同协程上下文代码链式调用
实现调整方案
核心问题原因
原有实现的问题主要有两个:
- ui函数的入参类型为泛型
T,当IO块返回可空类型时,T本身就是可空类型,即使做了非空判断再调用block,传递给用户的参数还是可空类型,需要手动二次校验 - 逻辑上没有做ui和ifNull块的互斥执行明确限制,导致需要在ui块里自己处理空分支的逻辑
调整后完整实现代码
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.launch @OptIn(ExperimentalCoroutinesApi::class) class CoroutineLinker<T> ( private val value: T?, private val scope: CoroutineScope ) { suspend infix fun <K> io(block: suspend () -> K?): CoroutineLinker<K?> { return withContext(Dispatchers.IO) { CoroutineLinker(block(), scope) } } suspend infix fun ui(block: suspend (value: T & Any) -> Unit): CoroutineLinker<T> { return withContext(Dispatchers.Main) { // 仅当value非空时执行ui块,且传递非空参数 value?.let { nonNullValue -> block(nonNullValue) } this@CoroutineLinker } } suspend infix fun ifNull(block: suspend () -> Unit) { withContext(Dispatchers.Main) { // 仅当value为空时执行 if (value == null) { block() } } } } // 可选便捷入口,无需手动创建CoroutineLinker实例 fun <T> CoroutineScope.launchLink(ioBlock: suspend () -> T?): Job { return launch { CoroutineLinker(ioBlock(), this@launchLink) } }
注:
T & Any是Kotlin 1.7+支持的泛型非空表示方法,用来获取泛型T的非空版本,如果项目Kotlin版本低于1.7,可以把参数类型改为T,实际运行时传递的也是非空值,仅编译期不会自动提示非空,不需要手动判空即可使用。
调用示例
完全符合预期的写法,ui块无需手动判空,ifNull块仅在IO返回空时执行:
// 建议使用和生命周期绑定的Scope,比如Android的lifecycleScope、viewModelScope lifecycleScope.launch { io { // IO操作,返回可空对象 getUserInfoFromServer(userId) } ui { nonNullUser -> // 此处拿到的nonNullUser自动为非空类型,直接使用即可 renderUserInfo(nonNullUser) } ifNull { // IO返回空时才会执行此处 showEmptyState() } }
优化建议
- 避免使用GlobalScope:传入和页面/组件生命周期绑定的协程Scope,页面销毁时自动取消未完成的协程任务,避免内存泄漏
- 新增异常捕获:可以给io、ui函数增加异常回调参数,捕获执行过程中的异常避免崩溃,示例如下:
suspend infix fun <K> io( block: suspend () -> K?, onError: suspend (Throwable) -> Unit = {} ): CoroutineLinker<K?> { return withContext(Dispatchers.IO) { val result = runCatching { block() } .onFailure { onError(it) } .getOrNull() CoroutineLinker(result, scope) } }
- 扩展调度器支持:可以按需新增
default、blockingIo等其他调度器的链式调用函数,覆盖计算密集型任务、阻塞IO任务等不同场景 - 支持返回值透传:如果需要多段IO、UI交替执行的链式调用,可以保留返回CoroutineLinker的逻辑,支持更长的链式调度流程
内容的提问来源于stack exchange,提问作者JonZarate
相关产品推荐
相关产品推荐

