封装回调的suspend方法异常:首次调用正常,后续调用逻辑停滞
问题描述
我为Stripe Kotlin/Java SDK的回调机制封装了如下挂起扩展方法:
suspend fun Terminal.discoverReaders(stripeTerminalConfiguration: DiscoveryConfiguration): List<Reader> = suspendCoroutine { continuation -> discoverReaders( stripeTerminalConfiguration, object : DiscoveryListener { override fun onUpdateDiscoveredReaders(readers: List<Reader>) { continuation.resume(readers) } }, object : Callback { override fun onFailure(e: TerminalException) { continuation.resumeWithException(e) } override fun onSuccess() { } } ) }
该方法首次调用可正常执行,但后续调用时,应用业务逻辑会神秘停滞,仅UI动画仍正常运行。已确保调用时使用了协程作用域,且添加了断点和日志排查,但未找到问题原因。

问题原因与修复方案
核心问题
你的封装存在两个致命问题:
continuation.resume被重复调用:DiscoveryListener.onUpdateDiscoveredReaders是多次回调的方法——每当有新读卡器被发现、已有读卡器断开连接时都会触发。但suspendCoroutine的continuation只能被resume一次,重复调用会导致协程内部状态损坏,后续调用时协程调度逻辑彻底紊乱。- 未终止底层扫描流程:Stripe Terminal的
discoverReaders是持续运行的扫描任务,你第一次调用时只在首次回调就恢复了协程,但底层扫描并未停止。后续再次调用时,新旧扫描流程冲突,占用SDK内部资源,导致业务逻辑阻塞。
修复后的代码
suspend fun Terminal.discoverReaders( config: DiscoveryConfiguration, timeoutMs: Long = 5000 ): List<Reader> = suspendCancellableCoroutine { continuation -> val listener = object : DiscoveryListener { private var hasResumed = false override fun onUpdateDiscoveredReaders(readers: List<Reader>) { // 仅在首次获取读卡器列表时恢复协程,避免重复调用 if (!hasResumed) { hasResumed = true continuation.resume(readers) // 恢复后立即终止扫描流程 stopDiscoverReaders() } } } val callback = object : Callback { override fun onFailure(e: TerminalException) { if (continuation.isActive) { continuation.resumeWithException(e) } } override fun onSuccess() { // 此回调仅表示扫描流程启动成功,无需处理 } } // 启动扫描 discoverReaders(config, listener, callback) // 协程取消时,确保终止扫描释放资源 continuation.invokeOnCancellation { stopDiscoverReaders() } // 添加超时逻辑,避免无限等待 withContext(Dispatchers.IO) { delay(timeoutMs) if (continuation.isActive) { stopDiscoverReaders() continuation.resume(emptyList()) } } }
关键修复说明
- 改用
suspendCancellableCoroutine:支持协程取消时执行资源清理逻辑,避免SDK资源泄漏 - 限制
continuation.resume单次调用:通过hasResumed标记防止重复恢复协程,避免状态异常 - 强制终止扫描流程:在协程恢复、取消或超时时,调用
stopDiscoverReaders()关闭底层扫描,释放SDK内部资源 - 新增超时机制:避免因无读卡器返回导致协程永久挂起
内容的提问来源于stack exchange,提问作者Alexander Trauzzi
相关产品推荐
相关产品推荐

