【问题标题】:How to call Kotlin suspending coroutine function from Java 7如何从 Java 7 调用 Kotlin 挂起协程函数
【发布时间】:2019-05-30 01:55:05
【问题描述】:

我正在尝试从 Java 7 调用 Kotlin 函数。我正在使用协程,这个被调用的函数正在挂起,例如:

suspend fun suspendingFunction(): Boolean {
    return async { longRunningFunction() }.await()
}

suspend fun longRunningFunction() : Boolean {
    delay(400)
    return true
}

我在 0.25.3 版本中使用协程,我可以通过将 Continuation<U> 实例作为参数传递给挂起函数来模拟简单的 Java 回调样式,例如

CoroutinesKt.suspendingFunction(new Continuation<Boolean>() {
    @Override
    public CoroutineContext getContext() {
        return EmptyCoroutineContext.INSTANCE;
    }

    @Override
    public void resume(Boolean value) {
        doSomethingWithResult(value);
    }

    @Override
    public void resumeWithException(@NotNull Throwable throwable) {
        handleException(throwable);
    }
});

但是,在更新到完全稳定的 1.0.1 版本后,我认为它不再可能了。假设暂停功能的更新版本如下所示:

suspend fun suspendingFunction(): Boolean {
    return GlobalScope.async { longRunningFunction() }.await()
}

Continuation&lt;U&gt; 现在使用 Result 类,这在 Java 中似乎无法使用(这是有道理的,因为它是内联类)。我试图使用协程中Continuation 的一些子类,但它们都是内部的或私有的。

我知道它通常是 advised to transform coroutine to CompletableFuture,但我使用的是 Android,这意味着只有 Java 7。另一方面,简单的Future 太笨了,因为我不想定期检查函数是否完成——我只想在它完成时被调用。而且我真的很想避免添加新库或许多其他类/方法。

有没有什么简单的方法可以直接从 Java 7 调用挂起函数?

由于 Kotlin 试图与 Java 进行非常好的互操作,我想会有一些简单的方法来做到这一点,但我还没有找到。

【问题讨论】:

    标签: java android kotlin coroutine kotlinx.coroutines


    【解决方案1】:

    根据您的环境,您有多种选择。

    1. 如果您在项目中使用RxJava2,模块kotlinx-coroutines-rx2 具有在协程和Rx 数据类型之间来回转换的实用函数。

    示例

    suspend fun sayHello(): String {
        delay(1000)
        return "Hi there"
    }
    
    fun sayHelloSingle(): Single<String> = GlobalScope.rxSingle { sayHello() }
    
    1. 否则,您可以添加一个新的Continuation 类,该类与旧类的定义相匹配,并且在 Java 端也很有用。

    示例(Kotlin 方面)

    abstract class Continuation<in T> : kotlin.coroutines.Continuation<T> {
        abstract fun resume(value: T)
        abstract fun resumeWithException(exception: Throwable)
        override fun resumeWith(result: Result<T>) = result.fold(::resume, ::resumeWithException)
    }   
    

    示例(Java 端)

    sayHello(new Continuation<String>() {
        @Override
        public CoroutineContext getContext() {
            return EmptyCoroutineContext.INSTANCE;
        }
    
        @Override
        public void resume(String value) {
            doSomethingWithResult(value);
        }
    
        @Override
        public void resumeWithException(@NotNull Throwable throwable) {
            doSomethingWithError(throwable);
        }
    });
    

    【讨论】:

    • 谢谢!虽然我不使用 RxJava,但在 Kotlin 端处理 Result 类是个好主意。额外的一门课只需付出很小的代价。
    • 反响很好。 Rx 是处理 Java 类中的挂起调用的好方法
    • 如果我从 Java Activity 调用它,我还应该使用 return EmptyCoroutineContext.INSTANCE; 吗?还是有更好的Context 可以使用?
    • 这个答案现在已经过时了,Continuation 接口已经改变,现在有一个方法resumeWith 在 Kotlin 端采用内联类,它可以代表成功或失败,但你可以'不要区分它们,因为您无权访问私有类。
    • @MarkoTopolnik 延续将作为一个非空的对象出现,但实际上它可以是空的并且将是你的对象,在这个例子中是一个字符串。
    【解决方案2】:

    您可以使用BuildersKt。这已经包含在implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0"

        BuildersKt.launch(
                GlobalScope.INSTANCE,
                (CoroutineContext) Dispatchers.getMain(),
                CoroutineStart.DEFAULT,
                (Function2<CoroutineScope, Continuation<? super Unit>, Unit>) (coroutineScope, continuation) -> {
                    // your code here
                    return Unit.INSTANCE;
                }
        );
    

    【讨论】:

      猜你喜欢
      • 2019-09-09
      • 2019-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-15
      • 2019-05-24
      • 2020-06-27
      • 2019-10-20
      相关资源
      最近更新 更多