如何在惯用的Kotlin中“包装”这个不完全的“通过惰性”结果缓存函数调用?

时间:2018-08-15 16:30:41

标签: kotlin kotlin-coroutines

我不能使用“ by lazy”,因为回调要求使用suspendCoroutine,如果它阻塞了主线程,则会在android中出现问题,因此我必须使用以下“缓存结果”模式一遍又一遍。有没有办法将其包装在funButUseCachedResultsIfTheyAlreadyExist模式中以封装xCached对象?

private var cameraDeviceCached: CameraDevice? = null

private suspend fun cameraDevice(): CameraDevice {
    cameraDeviceCached?.also { return it }
    return suspendCoroutine { cont: Continuation<CameraDevice> ->
        ... deep callbacks with cont.resume(camera) ...
    }.also {
        cameraDeviceCached = it
    }
}

当我真正想写的是

private suspend fun cameraDevice(): CameraDevice = theMagicFunction { cont ->
    ... deep callbacks with cont.resume(camera) ...
}

2 个答案:

答案 0 :(得分:2)

您可以通过包装如下的async调用来构建通用解决方案:

import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineStart.LAZY

class LazySuspendFun<out T>(
        scope: CoroutineScope,
        private val block: suspend () -> T
) {
    private val deferred = scope.async(Dispatchers.Unconfined, LAZY) { block() }

    suspend operator fun invoke() = deferred.await()
}

fun <T> CoroutineScope.lazySuspendFun(block: suspend () -> T) = 
        LazySuspendFun(this, block)

这是如何使用它的简单示例。请注意,我们能够对它们进行组合,以便我们将延迟初始化值用作获取另一个值的依赖项:

val fetchToken = lazySuspendFun<String> {
    suspendCoroutine { continuation ->
        Thread {
            info { "Fetching token" }
            sleep(3000)
            info { "Got token" }
            continuation.resume("hodda_")
        }.start()
    }
}

val fetchPosts = lazySuspendFun<List<String>> {
    val token = fetchToken()
    suspendCoroutine { continuation ->
        Thread {
            info { "Fetching posts" }
            sleep(3000)
            info { "Got posts" }
            continuation.resume(listOf("${token}post1", "${token}post2"))
        }
    }
}

在调用方,您必须位于一些协程上下文中,以便可以调用挂起函数:

myScope.launch {
   val posts = fetchPosts()
   ...
}

该解决方案足够强大,可以同时请求多次该值,并且初始化程序仅运行一次。

答案 1 :(得分:0)

我将以此为答案,因为不可能在注释中张贴太多代码。

您正在寻找的东西是这样的:

private suspend fun cameraDevice() = theMagicFunction {
    CameraDevice()
}()

suspend fun theMagicFunction(block: ()->CameraDevice): () -> CameraDevice {
    var cameraDeviceCached: CameraDevice? = null

    return fun(): CameraDevice {
        cameraDeviceCached?.also { return it }
        return suspendCoroutine { cont: Continuation<CameraDevice> ->
            cont.resume(block())
        }.also {
            cameraDeviceCached = it
        }
    }
}

不幸的是,由于闭包不能是可挂起的,而且也不是局部函数,因此无法编译。

除非可以,否则我最好建议,除非该变量使您感到困扰,否则请将其封装在类中。

相关问题