我正在使用协程编写一个应用程序(下面的代码大大简化了(。最近我看了《实践中的推论》的演讲,有点困惑。事实证明,我不知道何时使用CoroutineScope
的扩展函数,何时使用挂起函数。
我有一个中介(Presenter/ViewModel/Controller/etc(,它实现了CoroutineScope:
class UiMediator : CoroutineScope {
private val lifecycleJob: Job = Job()
override val coroutineContext = lifecycleJob + CoroutineDispatchersProvider.MAIN
// cancel parent Job somewhere
fun getChannel() {
launch {
val channel = useCase.execute()
view.show(channel)
}
}
}
业务逻辑(交互程序/用例(:
class UseCase {
suspend fun execute(): RssChannel = repository.getRssChannel()
}
和存储库:
class Repository {
suspend fun getRssChannel(): RssChannel {
// `getAllChannels` is a suspending fun that uses `withContext(IO)`
val channels = localStore.getAllChannels()
if (channels.isNotEmpty()) {
return channels[0]
}
// `fetchChannel` is a suspending fun that uses `suspendCancellableCoroutine`
// `saveChannel` is a suspending fun that uses `withContext(IO)`
return remoteStore.fetchChannel()
.also { localStore.saveChannel(it) }
}
}
所以我有几个问题:
- 我应该将
Repository#getRssChannel
声明为CoroutineScope的扩展函数吗(因为它产生了新的挂起函数:CCD_ 3,fetchChannel
、saveChannel
(?那么我该如何在UseCase
中使用它呢 - 我应该把
Repository#getRssChannel
包装成coroutineScope
函数以使所有派生的挂起作为后者的孩子 - 或者可能已经很好了,我什么都不应该改变。何时那么,将函数声明为
CoroutineScope
的扩展
挂起函数在完成任务后应该返回,它执行一些东西,可能需要一些时间,同时不会阻塞UI,完成后返回。
CoroutineScope扩展函数适用于即发即弃场景,您调用它,它生成一个协程并立即返回,同时任务继续执行。
问题1的答案:
不,您不应该将Repository#getRssChannel
声明为CoroutineScope
的扩展函数,因为您只调用挂起函数,而不启动(launch
/async
(新作业。正如@Francesc所解释的,CoroutineScope
的扩展函数只能启动新的作业,但不能立即返回结果,也不应该自己声明为suspend
。
问题2的答案:
不,您应该而不是将Repository#getRssChannel
包装成CoroutineScope
。只有在此方法中启动(launch
/async
(新的协程时,封装才有意义。新作业将是当前作业的子作业,外部方法只有在所有并行作业完成后才会返回。在您的情况下,您可以顺序调用其他挂起的协同程序,并且不需要新的作用域。
问题3的答案:
是的,你可以保留你的代码。如果您需要UiMediator#getChannel
的功能不止一次,那么此方法将是CoroutineScope
的扩展函数的候选者。