如何在非Activity类的Ktor Routing调用中使用registerForActivityResult获取Activity返回结果
解决方案
错误原因
registerForActivityResult 要求必须在 LifecycleOwner 到达 STARTED 状态前完成注册,你之前在 Ktor 路由的 POST 回调里才执行注册逻辑,此时 MainActivity 已经处于 RESUMED 状态,所以触发了 IllegalStateException。
实现思路
- 提前在 MainActivity 的 onCreate 阶段完成 ActivityResult 注册,将结果回调转发给 HttpServer
- 在 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
相关产品推荐
相关产品推荐

