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

如何在非Activity类的Ktor Routing调用中使用registerForActivityResult获取Activity返回结果

解决方案

错误原因

registerForActivityResult 要求必须在 LifecycleOwner 到达 STARTED 状态前完成注册,你之前在 Ktor 路由的 POST 回调里才执行注册逻辑,此时 MainActivity 已经处于 RESUMED 状态,所以触发了 IllegalStateException。

实现思路

  1. 提前在 MainActivity 的 onCreate 阶段完成 ActivityResult 注册,将结果回调转发给 HttpServer
  2. 在 HttpServer 中用 CompletableDeferred 桥接 Activity 回调和 Ktor 的挂起 respond 逻辑,实现请求等待结果返回后再响应

代码实现

1. 改造 MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var httpServer: HttpServer
    // 单次请求的结果等待对象,多并发请求可替换为带请求ID的Map
    private var resultDeferred: CompletableDeferred<ActivityResult>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 提前注册ActivityResult回调,符合Android生命周期要求
        val saleLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
            resultDeferred?.complete(result)
            resultDeferred = null
        }
        // 初始化HttpServer,传入封装好的启动+等待结果的挂起函数
        httpServer = HttpServer(
            launchActivity = { intent ->
                resultDeferred = CompletableDeferred()
                saleLauncher.launch(intent)
                resultDeferred!!.await()
            }
        )
        httpServer.start()
    }
}

2. 改造 HttpServer

class HttpServer(
    private val launchSaleActivity: suspend (Intent) -> ActivityResult
) {
    private val logger = LoggerFactory.getLogger(HttpServer::class.java.simpleName)
    private val server = createServer()
    // 绑定服务生命周期的协程作用域,替代GlobalScope避免内存泄漏
    private val serverScope = CoroutineScope(Dispatchers.IO + SupervisorJob())

    private suspend fun ApplicationCall.startSaleActivityForResult() {
        val intent = Intent()
        // 自行构造Intent的action、data等参数
        val result = launchSaleActivity(intent)
        if (result.resultCode == Activity.RESULT_OK || result.resultCode == Activity.RESULT_CANCELED) {
            val transactionResult = result.data
            // 路由上下文本身是挂起环境,直接调用respond即可
            respond(status = HttpStatusCode.OK, TransactionResponse())
        }
    }

    fun start() = server.start()

    fun stop() {
        serverScope.cancel()
        server.stop(0, 0)
    }

    private fun createServer(): NettyApplicationEngine {
        return serverScope.embeddedServer(Netty) {
            install(CallLogging)
            install(ContentNegotiation) {
                gson {
                    setPrettyPrinting()
                }
            }
            routing {
                post("/") {
                    call.startSaleActivityForResult()
                }
            }
        }
    }

    // 原有embeddedServer扩展方法保持不变,仅调整父协程上下文
    private fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration>
            CoroutineScope.embeddedServer(
        factory: ApplicationEngineFactory<TEngine, TConfiguration>,
        module: Application.() -> Unit
    ): TEngine {
        val environment = applicationEngineEnvironment {
            this.parentCoroutineContext = coroutineContext
            this.log = logger
            this.module(module)
            connector {
                this.port = 8081
            }
        }
        return embeddedServer(factory, environment)
    }
}

注意事项

  • 若需要支持多并发请求,可给每个请求生成唯一ID,存入Intent的Extra中,返回时从ActivityResult的data里取出ID匹配对应的CompletableDeferred即可
  • 建议用WeakReference包裹Activity上下文,避免长期持有导致内存泄漏
  • 可根据业务需要添加超时逻辑,避免请求长时间挂起

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 18:15:03