如何在同一协程中的动态代理中继续执行暂停功能?

时间:2019-01-31 19:03:30

标签: kotlin-coroutines

我想在同一协程的动态代理中继续执行暂停功能。 请看下面的代码:

interface Adder {
    suspend fun add(a: Int, b: Int): Int
}

val IH = InvocationHandler { _, method, args ->
    val continuation = args.last() as Continuation<*>
    val realArgs = args.take(args.size - 1)
    println("${method.name}$realArgs")
    GlobalScope.launch {
        delay(5_000)
        @Suppress("UNCHECKED_CAST") (continuation as Continuation<Int>).resume(3)
    }
    COROUTINE_SUSPENDED
}

fun main() {
    val adder = Proxy.newProxyInstance(
        Adder::class.java.classLoader, arrayOf(Adder::class.java), IH
    ) as Adder
    runBlocking {
        println(adder.add(1, 2))
    }
}

工作正常。它在新协程中运行延迟功能。 但是,那不是我想要的。

我想在与runBlocking一起启动的协程中运行InvocationHandler。 像这样:

val IH = InvocationHandler { _, _, _ ->
    delay(5_000)
    3
}

这显然不会编译,因为delay是必须在协程中运行的暂停函数。 所以问题是:如何为预期的行为编写InvocationHandler? 任何帮助将不胜感激。

我想在我的RPC框架中使用此代码。 我的真实代码将用无阻塞的Ktor套接字调用代替延迟调用,以通过网络串行化数据。 您可以在以下位置找到代码示例:https://raw.githubusercontent.com/softappeal/yass/master/kotlin/yass/test/ch/softappeal/yass/remote/SuspendProxy.kt

2 个答案:

答案 0 :(得分:0)

runBlocking内使用InvocationHandler

val IH = InvocationHandler { _, _, _ ->

    runBlocking{
        delay(5_000)// now you can use suspend functions here
    }

    3
}

答案 1 :(得分:0)

我已经找到解决问题的方法:

package ch.softappeal.yass

import kotlinx.coroutines.*
import java.lang.reflect.*
import kotlin.coroutines.*
import kotlin.test.*

typealias SuspendInvoker = suspend (method: Method, arguments: List<Any?>) -> Any?

private interface SuspendFunction {
    suspend fun invoke(): Any?
}

private val SuspendRemover = SuspendFunction::class.java.methods[0]

@Suppress("UNCHECKED_CAST")
fun <C : Any> proxy(contract: Class<C>, invoker: SuspendInvoker): C =
    Proxy.newProxyInstance(contract.classLoader, arrayOf(contract)) { _, method, arguments ->
        val continuation = arguments.last() as Continuation<*>
        val argumentsWithoutContinuation = arguments.take(arguments.size - 1)
        SuspendRemover.invoke(object : SuspendFunction {
            override suspend fun invoke() = invoker(method, argumentsWithoutContinuation)
        }, continuation)
    } as C

interface Adder {
    suspend fun add(a: Int, b: Int): Int
}

class SuspendProxyTest {
    @Test
    fun test() {
        val adder = proxy(Adder::class.java) { method, arguments ->
            println("${method.name}$arguments")
            delay(100)
            3
        }
        runBlocking { assertEquals(3, adder.add(1, 2)) }
    }
}

有任何评论吗?
这是一个好的/问题解决方案吗?
是否可以/应该将“删除暂挂功能”添加到kotlin.coroutines库中?