Reputation: 6948
Assume we have the following suspend function:
suspend fun doSomething(): List<MyClass> { ... }
If I want to call this function in one of my existing Java classes (which I'm not able to convert to Kotlin for now) and get its return value I have to provide a Continuation<? super List<MyClass>>
as its parameter (Obviously).
My question is, How can I implement one. Specially its getContext
getter.
Upvotes: 104
Views: 60447
Reputation: 321
If you dont want to use org.jetbrains.kotlinx:kotlinx-coroutines-jdk8
, I have a new idea.
Write below code in your kotlin project.
@JvmOverloads
fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
}
I wrote it in my Coroutines
class
Then you can call your suspend function like:
Coroutines coroutines = new Coroutines(); // My coroutines utils class
UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
(tokenResult, throwable) -> {
System.out.println("Coroutines finished");
System.out.println("Result: " + tokenResult);
System.out.println("Exception: " + throwable);
}
));
login() function is a suspend function.
suspend fun login(username: String, password: String): TokenResult
For your code, you can:
doSomething(getContinuation((result, throwable) -> {
//TODO
}));
Besides, you may want to run your callback code in a different thread (e.g. Main thread), just use launch(Dispathers.Main)
to wrap resumeWith()
...
override fun resumeWith(result: Result<R>) {
launch(Dispathers.Main) {
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
...
Update: My friend has developed a plugin kotlin-jvm-blocking-bridge that can automatically generate blocking bridges for calling suspend functions from Java with minimal effort, also give it a try.
Upvotes: 17
Reputation: 1122
I created interface class based on @Kenvix answer to make it compatible with old Android SDK (lower than API 24)
interface CoroutineCallback<RESULT> {
companion object {
@JvmOverloads
fun <R> call(
callback: CoroutineCallback<R>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
callback.onComplete(result.getOrNull(), result.exceptionOrNull())
}
}
}
}
fun onComplete(result: RESULT?, error: Throwable?)
}
usage
class kotlinClass {
suspend doSomething(foo, bar) : FooBar {}
}
class javaClass {
void doSomething(){
kotlinClassObject.doSomething("foo", "bar", CoroutineCallback.Companion.call((fooBar, error) -> {
//do something with result or error
}));
}
}
now call suspend function from any java class by passing CoroutineCallback
Upvotes: 4
Reputation: 642
For coroutines 1.3.0 use this:
BuildersKt.launch(GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
(coroutineScope, continuation) -> suspendFunction(arguments)
);
For java < 8:
BuildersKt.launch(
GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
new Function2<CoroutineScope, Continuation<? super Unit>, Unit/*or your return type here*/>() {
@Override
public Unit/*or your return type here*/ invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
//do what you want
return Unit.INSTANCE; //or something with the defined type
}
}
);
My gradle file:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
Kotlin uses static classes for extension functions, launch is an extension function, so it is defined in BuildersKt. The first parameter is the target of the extension function, the rest are the parameters from the extension functions.
Upvotes: 14
Reputation: 28628
First, add org.jetbrains.kotlinx:kotlinx-coroutines-jdk8
module to your dependencies. In your Kotlin file define the following async function that corresponds to Java style of writing async APIs:
fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
GlobalScope.future { doSomething() }
Now use doSomethingAsync
from Java in the same way as you are using other asynchronous APIs in the Java world.
Upvotes: 106