如何在较低级别的管理器中获取协程范围 class
How to get coroutine scope in lower level manager class
我的一个图书馆中有一个数据管理器 class。到目前为止,它一直在与执行者合作并拥有自己的执行者。现在 class 正在转换为协程。
当管理器必须异步初始化数据时出现问题,如下例所示。为了执行挂起函数,对 suspendedInit
的调用必须包含在 launch
中。这里的问题是管理器没有自己的协程范围。
有些我正在看但不喜欢:
- 将协程范围传递给
DataManager
构造函数并使用它。 DataManager
的用户尚不支持协同程序,因此没有自己的范围。
DataManager
实施 CoroutineScope
并创建自己的范围。 CoroutineScope
文档中不鼓励这样做。
这个问题的推荐解决方案是什么?
class DataManager {
init {
suspendedInit()
}
private suspend fun suspendedInit() = withContext(Dispatchers.IO) {
/* some long asynchronous operations here */
}
}
您可以像这样向 DataManager
添加一个 coroutineScope
字段:
private val coroutineScope = CoroutineScope(Dispatchers.IO)
然后从该范围调用 suspendedInit()
。
DataManager implementing CoroutineScope and creating its own scope. This is discouraged as documented by CoroutineScope documentation.
如果最初的 DataManager
有自己的执行器,它很可能也有自己的生命周期,并有关闭此执行器的正确方法。
在这种情况下,创建您自己的 CoroutineScope
完全可以,就像您创建执行器的方式一样(实际上它可以完全基于您的初始执行器)。唯一不鼓励的情况是你没有正确处理范围的生命周期:
CoroutineScope should be declared as a property on entities with a well-defined lifecycle that are responsible for launching children coroutines.
The key part of custom usage of CustomScope is cancelling it and the end of the lifecycle. The CoroutineScope.cancel extension function shall be used when the entity that was launching coroutines is no longer needed.
class DataManager {
private val executor = TODO("original executor definition")
val coroutineScope = executor.asCoroutineDispatcher()
init {
coroutineScope.launch {
suspendedInit()
}
}
private suspend fun suspendedInit() = ...
fun close() {
// executor.shutdown() // <-- not needed anymore
coroutineScope.cancel()
}
}
如果您不需要独立的线程池,您也可以完全摆脱执行程序。在这种情况下,您可以使用 CoroutineScope(Dispatchers.IO)
(如果您想要对所有协程使用 IO 调度程序)或 CoroutineScope(Dispatchers.Default)
(如果您打算主要启动 CPU-bound)来初始化作用域任务)并在必要时使用 withContext(IO)
。
我的一个图书馆中有一个数据管理器 class。到目前为止,它一直在与执行者合作并拥有自己的执行者。现在 class 正在转换为协程。
当管理器必须异步初始化数据时出现问题,如下例所示。为了执行挂起函数,对 suspendedInit
的调用必须包含在 launch
中。这里的问题是管理器没有自己的协程范围。
有些我正在看但不喜欢:
- 将协程范围传递给
DataManager
构造函数并使用它。DataManager
的用户尚不支持协同程序,因此没有自己的范围。 DataManager
实施CoroutineScope
并创建自己的范围。CoroutineScope
文档中不鼓励这样做。
这个问题的推荐解决方案是什么?
class DataManager {
init {
suspendedInit()
}
private suspend fun suspendedInit() = withContext(Dispatchers.IO) {
/* some long asynchronous operations here */
}
}
您可以像这样向 DataManager
添加一个 coroutineScope
字段:
private val coroutineScope = CoroutineScope(Dispatchers.IO)
然后从该范围调用 suspendedInit()
。
DataManager implementing CoroutineScope and creating its own scope. This is discouraged as documented by CoroutineScope documentation.
如果最初的 DataManager
有自己的执行器,它很可能也有自己的生命周期,并有关闭此执行器的正确方法。
在这种情况下,创建您自己的 CoroutineScope
完全可以,就像您创建执行器的方式一样(实际上它可以完全基于您的初始执行器)。唯一不鼓励的情况是你没有正确处理范围的生命周期:
CoroutineScope should be declared as a property on entities with a well-defined lifecycle that are responsible for launching children coroutines.
The key part of custom usage of CustomScope is cancelling it and the end of the lifecycle. The CoroutineScope.cancel extension function shall be used when the entity that was launching coroutines is no longer needed.
class DataManager {
private val executor = TODO("original executor definition")
val coroutineScope = executor.asCoroutineDispatcher()
init {
coroutineScope.launch {
suspendedInit()
}
}
private suspend fun suspendedInit() = ...
fun close() {
// executor.shutdown() // <-- not needed anymore
coroutineScope.cancel()
}
}
如果您不需要独立的线程池,您也可以完全摆脱执行程序。在这种情况下,您可以使用 CoroutineScope(Dispatchers.IO)
(如果您想要对所有协程使用 IO 调度程序)或 CoroutineScope(Dispatchers.Default)
(如果您打算主要启动 CPU-bound)来初始化作用域任务)并在必要时使用 withContext(IO)
。