如何在非 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 服务器或存储库对象之前被销毁。
如何从另一个 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.
上没有 onCreatejava.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:
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 服务器或存储库对象之前被销毁。