如何在非 activity class 中使用 ktor 的路由调用 运行 中的 registerForActivityResult 获取结果?

How to get result using registerForActivityResult from within ktor's Routing call running in a non-activity class?

如何从另一个 activity (registerForActivity) 中获取结果来自 ktor 的路由 API 调用(例如 /POST)运行ning 在非activity class?

背景:对于Android应用程序,我运行ktor服务器引擎'netty'在非activityclassHttpServer.kt。我需要在 ktor 的路由 POST 处理程序中调用另一个应用程序的 activity,所以我从 MainActivity.kt 传递 'appCompatActivity'。这样做是因为,我假设 registerForActivityResult() 依赖于 UI/life 循环 class.

当 运行 如下所示时出现问题,因为 registerForActivityResult() 需要 运行 更早(比如 onCreate() ?),而我没有这样的 class在此非activityclass。而且ActivityResult返回时回调运行需要调用ktor ApplicationCall的respond也是一个suspend函数

class HttpServer(
    private val applicationContext: AppCompatActivity
) {
    private val logger = LoggerFactory.getLogger(HttpServer::class.java.simpleName)
    private val server = createServer()

    private fun ApplicationCall.startSaleActivityForResult() {    //    <======   *
        val activityLauncherCustom =
            applicationContext.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
                if (result.resultCode == Activity.RESULT_OK || result.resultCode == Activity.RESULT_CANCELED) {
                    val transactionResultReturned = result.data
                    // Handle the returned result properly using transactionResultReturned
                    GlobalScope.launch {
                        respond(status = HttpStatusCode.OK, TransactionResponse())
                    }
                }
            }
        val intent = Intent()    
        // Ignoring statements to create proper action/data intent
        activityLauncherCustom.launch(intent)    //    <======   *
    }

    fun start() = server.start()

    fun stop() = server.stop(0, 0)

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

    private fun <TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration>
            CoroutineScope.embeddedServer(
        factory: ApplicationEngineFactory<TEngine, TConfiguration>,
        module: Application.() -> Unit
    ): TEngine {
        val environment = applicationEngineEnvironment {
            this.parentCoroutineContext = coroutineContext + parentCoroutineContext
            this.log = logger
            this.module(module)

            connector {
                this.port = 8081
            }
        }
        return embeddedServer(factory, environment)
    }
}

上面是我试过的,但给出了以下错误。而且我在这个非 activity class.

上没有 onCreate

java.lang.IllegalStateException: LifecycleOwner com.youtap.upti.MainActivity@38dcf06 is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.

如有任何解决此问题的建议,我们将不胜感激。

下面与上面相同的代码片段作为屏幕截图,显示来自 Android Studio 的 declaration/param 类型的帮助文本:

然后我从 MainActivity 的 onCreate() 调用此服务器 class:

要解决您的问题并隐藏复杂性,您可以创建一个中间 class 来启动 activity 并等待结果的出现:

import kotlinx.coroutines.channels.Channel

class Repository(private val activity: MainActivity) {
    private val channel = Channel<Int>(1)

    suspend fun get(input: String): Int {
        activity.activityLauncher.launch(input)
        return channel.receive()
    }

    suspend fun callback(result: Int) {
        channel.send(result)
    }
}

您可以在 MainActivity class:

中存储对存储库和 activity 启动器的引用
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        CoroutineScope(Dispatchers.IO).launch {
            HttpServer(this@MainActivity).also { it.start() }
        }
    }

    val activityLauncher = registerForActivityResult(MySecondActivityContract()) { result ->
        GlobalScope.launch {
            repository.callback(result!!)
        }
    }

    val repository = Repository(this)
}

我的第二个 activity 和合同如下所示:

class ChildActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_child)

        val result = Intent()
        result.putExtra("name", 6666)
        result.data = Uri.parse("http://mydata")
        setResult(Activity.RESULT_OK, result)
        finish()
    }
}

class MySecondActivityContract : ActivityResultContract<String, Int?>() {

    override fun createIntent(context: Context, input: String?): Intent {
        return Intent(context, ChildActivity::class.java)
            .putExtra("my_input_key", input)
    }

    override fun parseResult(resultCode: Int, intent: Intent?): Int? = when {
        resultCode != Activity.RESULT_OK -> null
        else -> intent?.getIntExtra("name", 42)
    }

    override fun getSynchronousResult(context: Context, input: String?): SynchronousResult<Int?>? {
        return if (input.isNullOrEmpty()) SynchronousResult(42) else null
    }
}

最简单的部分是路由处理程序:

routing {
    route("/") {
        post {
            val result = (applicationContext as MainActivity).repository.get("input")
            call.respondText { result.toString() }
        }
    }
}

此解决方案有效,但同时只处理一个请求,而且不可靠,因为 Activity 可能会在 HTTP 服务器或存储库对象之前被销毁。