首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何从运行在非活动类中的ktor路由调用中获得使用registerForActivityResult的结果?

如何从运行在非活动类中的ktor路由调用中获得使用registerForActivityResult的结果?
EN

Stack Overflow用户
提问于 2021-10-17 22:22:03
回答 1查看 1.5K关注 0票数 0

如何从ktor的路由API调用中获得另一个活动(registerForActivity)的结果(例如。( /POST)在非活动类中运行?

背景:对于一个安卓应用程序,我在一个非活动类HttpServer.kt中运行ktor服务器引擎'netty‘。我需要调用另一个应用程序的活动,从with in ktor的路由‘POST处理程序,所以我传递'appCompatActivity’从MainActivity.kt。这样做是因为,我猜想,registerForActivityResult()依赖于UI/生命周期类。

当按照下面的方式运行时会出现问题,因为registerForActivityResult()需要更早地运行(比如onCreate() ?),而且我在这个非活动类中没有这样的类。此外,返回ActivityResult时要运行的回调需要调用ktor ApplicationCall的响应,这也是一个挂起函数。

代码语言:javascript
复制
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)
    }
}

以上是我尝试过的,但给出了下面的错误。我在这个非活动类上没有onCreate。

java.lang.IllegalStateException:当当前状态恢复时,LifecycleOwner com.youtap.upti.MainActivity@38dcf06正在尝试注册。LifecycleOwners必须在启动前调用寄存器。

任何解决这一问题的建议都将不胜感激。

下面同样是上面的片段,作为屏幕截图,在Android的声明/param类型上显示辅助文本:

我从onCreate()的MainActivity调用这个服务器类:

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-18 13:55:48

为了解决问题并隐藏复杂性,您可以创建一个中间类,用于启动活动并等待结果的到来:

代码语言:javascript
复制
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类中存储对存储库和活动启动程序的引用:

代码语言:javascript
复制
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)
}

我的第二项活动和合同如下:

代码语言:javascript
复制
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
    }
}

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

代码语言:javascript
复制
routing {
    route("/") {
        post {
            val result = (applicationContext as MainActivity).repository.get("input")
            call.respondText { result.toString() }
        }
    }
}

这个解决方案可以工作,但是只有一个请求是同时处理的,而且它不健壮,因为Activity可能在HTTP或存储库对象之前被销毁。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69609046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档