从Java调用中返回Kotlin协程的返回值(Int)。

huangapple go评论68阅读模式
英文:

Returning value (Int) from Kotlin coroutine from Java call

问题

我已经努力了很长时间,试图从 Kotlin 协程中将一个值返回给 Java,但感觉时间太长了。这是我最终使其工作的方式,但我并不100%确定这是否是最佳方式。我在**返回到 Kotlin 时**获取返回值的方式不起作用。我只是用 SQLite 数据库中的总行数更新 UI。

这是我从 Java(活动)的调用,来自主线程

    tv.setText(String.valueOf(DbUtil.getDbRowCount()));

这是在 DbUtil(Kotlin 对象类)中的方法

    @JvmStatic
    fun getDbRowCount(): Int = runBlocking {
        withContext(IO){DbHelper.getInstance().dbRowCount}
    }

我的问题:
1)添加 withContext(IO) 是否可以阻止主线程被阻塞?
2)如果实际上会阻塞,我该如何重写代码以避免阻塞。
英文:

I've been struggling to return a value from a Kotlin coroutine back to Java for what feels like way too long. This is how I finally got it to work, but am not 100% sure that it is the best way. The ways I get a return value when returning to Kotlin are not working. I'm simply updating the UI with the total row count in the SQLite db.

Here's my call from Java (activity) from main thread

tv.setText(String.valueOf(DbUtil.getDbRowCount()));

And here's the method in DbUtil (Kotlin object Class)

@JvmStatic
fun getDbRowCount(): Int = runBlocking {
    withContext(IO){DbHelper.getInstance().dbRowCount}
}

My questions:

  1. Does adding the withContext(IO) keep the main thread from being blocked?
  2. If this does in fact block, how can I rewrite this so that it doesn't.

答案1

得分: 3

使用runBlocking做了它所说的事情,会阻塞等待结果。所以在IO调度程序上进行调度没有效果。

挂起函数只能被协程调用,因此您必须在协程内部才能调用任何可挂起的函数。

  1. 首先要移除屏障,即使函数能够从非协程体中调用:
@JvmStatic
val scope = CoroutineScope(EmptyCoroutineContext)

@JvmStatic
fun getDbRowCount(): Deferred<Int> = scope.async(Dispatchers.IO) {
    DbHelper.getInstance().dbRowCount
}

如果您在协程中(在Kotlin代码中),可以简单地调用.await()来获取Int结果(挂起)。

  1. 为了使结果与Java互操作,您可以将协程中的Deferred转换为CompletableFuture
// 需要:最新的$version截至目前为止是1.3.9
// implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$version"
CompletableFuture cf = FutureKt.asCompletableFuture(getDbRowCount());
  1. 然后,您可以使用回调样式来监听结果:
cf.whenCompleteAsync((result, exception) -> {
    // 确保UI更改在主/ UI线程上发生。
    new Handler(context.getMainLooper()).post(() -> tv.setText(String.valueOf(result)));
});
英文:

Using runBlocking does what it says, blocks for the result. So dispatching to the IO dispatcher has no effect.

A suspending function can only be callable by a coroutine, so you must be inside a coroutine in order to call any suspendable function.

  1. First to remove the barrier, i.e. to make the function callable from non-coroutine body:
@JvmStatic
val scope = CoroutineScope(EmptyCoroutineContext)

@JvmStatic
fun getDbRowCount(): Deferred&lt;Int&gt; = scope.async(Dispatchers.IO) {
    DbHelper.getInstance().dbRowCount
}

If you are on a coroutine (in Kotlin code) you can simply call .await() to get Int result (suspending).

  1. To make the result interoperable with Java, you can convert the Deferred from the coroutine to a CompletableFuture.
// requires: latest $version as of now is 1.3.9
// implementation &quot;org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$version&quot;
CompletableFuture cf = FutureKt.asCompletableFuture(getDbRowCount());
  1. Then you can use the callback-style to listen to the result:
cf.whenCompleteAsync((result, exception) -&gt; {
    // Ensure UI change happens on Main/UI thread.
    new Handler(context.getMainLooper()).post(() -&gt; tv.setText(String.valueOf(result)));
});

huangapple
  • 本文由 发表于 2020年10月10日 23:19:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/64295025.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定