当前位置: 首页 > 知识库问答 >
问题:

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

宁飞宇
2023-03-14

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

背景:对于一个Android应用程序,我在一个非活动类HttpS中运行ktor服务器引擎'netty'erver.kt.我需要从ktor的路由'POST处理程序中调用另一个应用程序的活动,所以我从MainA传递'appCompatactive'ctivity.kt.这就完成了,只是因为,我假设,registerForActivityResult()依赖于UI/生命周期类。

按如下方式运行时会出现问题,因为registerForActivityResult()需要提前运行(如onCreate()?),我在这个非活动课上没有这样的课。此外,在返回ActivityResult时运行的回调需要调用ktor ApplicationCall的respond,它也是一个挂起函数。

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.IllegalState例外:LifecycleOwner com。youtap。正常运行时间。MainActivity@38dcf06正在尝试在恢复当前状态时注册。生命周期所有者在启动之前必须调用register。

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

共有1个答案

邵星光
2023-03-14

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

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)
    }
}

您可以在MainActive类中存储对存储库和活动启动器的引用:

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)
}

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

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() }
        }
    }
}

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

 类似资料:
  • 我已经创建了一个片段,当点击一个按钮时,会弹出一个类似对话框的ALTERVIEW。在该视图中包括谷歌登录按钮。当我连接到firebase并在startActivityForResult之后继续执行时,我需要重写onActivityResult方法。但弹出对应的类扩展了另一个对话框类,不能重写onActivityResult方法。当要重写时,它显示方法并没有从其超类错误重写方法。如何解决这个问题。以

  • 在我的非活动类中,我请求了一个Fine location权限,但回调onRequestPermissionsResult“永远不会被调用”。现在我看到了一些与此相关的问题,但他们都认为请求是从活动或片段发出的,没有人考虑从非活动类发出请求。这是我的代码 这是onRequestPermissionResultCallback方法的实现 解决了:正如一些人提到的“activity”的onRequest

  • 注意:这里有很多不同的答案,而且大多数在某个时候都是有效的。事实上,当Angular团队改变其路由器时,工作的方式已经改变了很多次。Router3.0版本将最终成为Angular中的路由器,它打破了许多这些解决方案,但提供了一个非常简单的解决方案。从RC.3开始,首选解决方案是使用,如本答案所示。 在Angular应用程序中(我写这篇文章的时候是2.0.0-Beta.0版本中的最新版本),如何确定

  • 问题内容: 因此,我可以很好地做到这一点: 如果存在。我也可以高兴地做到这一点: 如果类文件存在于jar的适当部分中。简单的东西。但是我无法为自己的生活做这样的事情: 存在的地方和存在的地方(当然不包含MyClass)。 我会感到愚蠢吗? 问题答案: 可能:) 基本上,这只是在类路径和jar文件中包括(当前目录)。

  • 问题内容: 我想获取方法的调用者类,即 在方法栏中,我需要获取类名称,然后找到了该方法: 但是,即使是,当我尝试调用它时,Eclipse 也会说: 访问限制:由于必需的库C:\ Program Files \ Java \ jre7 \ lib \ rt.jar的限制,无法访问类型为Reflection的方法getCallerClass() 还有其他选择吗? 问题答案: 你可以生成堆栈跟踪并使用S

  • 本文向大家介绍如何使用python在Selenium中的工作簿中获取活动表?,包括了如何使用python在Selenium中的工作簿中获取活动表?的使用技巧和注意事项,需要的朋友参考一下 我们可以在Selenium的工作簿中获得活动表。Excel是电子表格,扩展名为.xlsx。一个excel工作簿有多个工作表,每个工作表都由行和列组成。 在所有工作表中,当我们访问一个称为活动工作表的特定工作表时。