You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

封装回调的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动画仍正常运行。已确保调用时使用了协程作用域,且添加了断点和日志排查,但未找到问题原因。

协程停滞相关截图

问题原因与修复方案

核心问题

你的封装存在两个致命问题:

  1. continuation.resume被重复调用:DiscoveryListener.onUpdateDiscoveredReaders是多次回调的方法——每当有新读卡器被发现、已有读卡器断开连接时都会触发。但suspendCoroutine的continuation只能被resume一次,重复调用会导致协程内部状态损坏,后续调用时协程调度逻辑彻底紊乱。
  2. 未终止底层扫描流程: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 02:33:17