How to call Kotlin suspending coroutine function from Java 7

13,682

Solution 1

You have several options depending on your environment.

  1. If you are using RxJava2 in the project, the module kotlinx-coroutines-rx2 has utility functions to convert back and forth between coroutines and Rx datatypes.

Example

suspend fun sayHello(): String {
    delay(1000)
    return "Hi there"
}

fun sayHelloSingle(): Single<String> = GlobalScope.rxSingle { sayHello() }
  1. Otherwise, you could add a new Continuation class that matches the definition of the old one and is also useful in the Java side.

Example (Kotlin side)

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

Example (Java side)

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

Solution 2

You can use BuildersKt. That is already included in the 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;
            }
    );
Share:
13,682
michalbrz
Author by

michalbrz

I do Android programming by day and every other programming at night. Suprisingly, I also care about code quality.

Updated on June 07, 2022

Comments

  • michalbrz
    michalbrz almost 2 years

    I'm trying to call Kotlin function from Java 7. I'm using coroutines and this called function is suspending, for example:

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

    I was using coroutines in version 0.25.3 and I could emulate simple Java callback style by passing Continuation<U> instance as an argument to suspending function, e.g.

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

    However, after updating to fully stable 1.0.1 release, I think it's no longer possible. Let's say updated version of suspending function looks like that:

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

    Continuation<U> now uses Result class, which seems to be unusable from Java (which makes sense as it is inline class). I was trying to use some subclass of Continuation from coroutines but they are all internal or private.

    I know that usually it is advised to transform coroutine to CompletableFuture, but I'm on Android, which means Java 7 only. Simple Future on the other hand is too dumb as I don't want to check periodically if function is finished - I just want to be called when it is finished. And I would really like to avoid adding new libraries or many additional classes/methods.

    Is there any simple way to call suspending function directly from Java 7?

    As Kotlin tries to be very interoperable with Java I would imagine there would be some easy way to do that, but I'm yet to find it.

  • michalbrz
    michalbrz over 5 years
    Thanks! While I don't use RxJava, handling Result class on Kotlin side is very good idea. And one additional class is small price to pay.
  • Ricard
    Ricard about 5 years
    Great response. Rx is good and clean approach to handle suspend calls inside java classes
  • casolorz
    casolorz over 4 years
    If I'm calling this from a Java Activity should I still be using return EmptyCoroutineContext.INSTANCE;? or is there a better Context to use?
  • Marko Topolnik
    Marko Topolnik over 4 years
    This answer is now outdated, the Continuation interface has changed and now has a single method resumeWith that takes an inline class on the Kotlin side, it can represent either success or failure but you can't distinguish them because you have no access to the private classes.
  • Nathan Schwermann
    Nathan Schwermann about 4 years
    @MarkoTopolnik the continuation will come up as an Object that is non null but actually it can be null and will be you Object, in this example a String.
  • Marko Topolnik
    Marko Topolnik about 4 years
    @NathanSchwermann If the suspendable function throws an exception, the continuation is resumed with kotlin.Result$Failure, which is an internal class.
  • tim4dev
    tim4dev almost 4 years
    Great! It worked kotlin-stdlib-jdk7, kotlinx-coroutines-android:1.3.6
  • tim4dev
    tim4dev almost 4 years
    For RxJava you can simply call rxSingle { sayHello() }
  • shtas
    shtas about 2 years
    how do I switch dispatcher context inside the result block (your code here)? I need to read the data from the db on the background thread but afterwards display it to the user (android uI).