How to run suspend method via reflection?

2019-07-17 09:56发布

There is an coroutine block that can runs suspend functions.

But I call the function by invoke via reflection. This is java style invocation, apparently a simple call will not work. Are there ways to run reflected method asynchronously? How to await this method?

import kotlin.coroutines.experimental.*

class TestClass(val InString: String) {
    suspend fun printString() {
        println(InString)
    }
}

fun launch(context: CoroutineContext, block: suspend () -> Unit) =
        block.startCoroutine(StandaloneCoroutine(context))

private class StandaloneCoroutine(override val context: CoroutineContext): Continuation<Unit> {
    override fun resume(value: Unit) {}

    override fun resumeWithException(exception: Throwable) {
        val currentThread = Thread.currentThread()
        currentThread.uncaughtExceptionHandler.uncaughtException(currentThread, exception)
    }
}

fun main(args: Array<String>) {
    launch(EmptyCoroutineContext) {
        val a = TestClass("TestString");

        for (method in a.javaClass.methods) {
            if (method.name == "printString")
                method.invoke(a)  // Exception in thread "main" java.lang.IllegalArgumentException: wrong number of arguments 

        }
    }
}

1条回答
萌系小妹纸
2楼-- · 2019-07-17 10:25

Update

Since Kotlin 1.3 reflection natively supports calling suspending functions via KFunction.callSuspend and KFunction.callSuspendBy, so the above workaround is no longer needed.

Original Answer

Every suspend method in Kotlin is represented on JVM via CPS transformation that is explained in the coroutines design document. Java reflection is not aware about it and Kotlin reflection does not currently provide convenient means to perform invocation of suspending function either.

You'll have to do invocation with CPS transformation yourself via helper function. I'd suggest to implement the following helper for this purpose:

import java.lang.reflect.Method
import kotlin.coroutines.experimental.intrinsics.*

suspend fun Method.invokeSuspend(obj: Any, vararg args: Any?): Any? =
    suspendCoroutineOrReturn { cont ->
        invoke(obj, *args, cont)
    }

Now if you replace invoke with invokeSuspend in your code, then it is going to work just like expected.

查看更多
登录 后发表回答