协同程序中的范围混淆
Scope confused in coroutines
我有一个想使用协程的用例,但对如何实现它有点困惑。
一个具有作用域并绑定到 UI 生命周期并从存储库调用 API 的 ViewModel:
class UserViewModel(): CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun showUser() {
launch {
val user = repo.getUser()
livedata = user
}
}
fun onClean() {
job.cancel()
}
}
存储库使用协程构建网络调用,如下所示:
suspend fun getUser() = GlobalScope { ... }
用例是一旦从 ViewModel 调用 API 就需要始终完全执行存储库功能,因为我们需要从服务器捕获所有网络响应。
我如何确保存储库中的协程始终执行,但 ViewModel 协程将被取消以避免视图模型被清除后内存泄漏?
根据 GlobalScope
的文档,我认为我们可以依靠使用全局 CoroutineScope 启动的协程始终执行。文档说:
Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely.
我已经实现了一些测试代码,当 job
在 UserViewModel
中被取消时,存储库中的协程继续执行。这是带有我的评论的代码:
class UserViewModel(): CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun showUser() {
launch {
val repo = Repository()
val userDeferred = repo.getUser()
// if onClean() is called before the coroutine in Repository finishes,
// this line will not be called, but coroutine in Repository will continue executing
val result = userDeferred.await() // wait for result of I/O operation without blocking the main thread
}
}
fun onClean() {
job.cancel()
}
}
class Repository {
fun getUser() = GlobalScope.async {
delay(4000)
// this line is executed no matter whether the job in UserViewModel was canceled or not
"User returned"
}
}
另外我们可以减少showUser()
函数:
fun showUser() = repo.getUser().then(this) {
// `it` contains the result
// here is the main thread, use `it` to update UI
}
使用扩展函数then
:
fun <T> Deferred<T>.then(scope: CoroutineScope = GlobalScope, uiFun: (T) -> Unit) {
scope.launch { uiFun(this@then.await()) }
}
如果您为 Android 开发并且想要确保即使在清理 ViewModel 之后您的 IO 操作也能完全执行,请使用 WorkManager。它适用于异步和可延迟的任务,这些任务需要保证即使应用程序退出,系统也会 运行 它们。
ViewModel 只能在配置更改后存活,一般情况下不会在 activity 破坏后存活。
如果您希望您的操作在 activity 破坏后继续,您应该使用生命周期超过 activity 的组件,即 Service。
此外,如果您想确保您的操作 "always" 已执行,您应该使用前台服务,该服务在 运行.
时需要有一个不可关闭的通知
A started service can use the startForeground(int, Notification)
API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory.
我有一个想使用协程的用例,但对如何实现它有点困惑。
一个具有作用域并绑定到 UI 生命周期并从存储库调用 API 的 ViewModel:
class UserViewModel(): CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun showUser() {
launch {
val user = repo.getUser()
livedata = user
}
}
fun onClean() {
job.cancel()
}
}
存储库使用协程构建网络调用,如下所示:
suspend fun getUser() = GlobalScope { ... }
用例是一旦从 ViewModel 调用 API 就需要始终完全执行存储库功能,因为我们需要从服务器捕获所有网络响应。
我如何确保存储库中的协程始终执行,但 ViewModel 协程将被取消以避免视图模型被清除后内存泄漏?
根据 GlobalScope
的文档,我认为我们可以依靠使用全局 CoroutineScope 启动的协程始终执行。文档说:
Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely.
我已经实现了一些测试代码,当 job
在 UserViewModel
中被取消时,存储库中的协程继续执行。这是带有我的评论的代码:
class UserViewModel(): CoroutineScope {
private val job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun showUser() {
launch {
val repo = Repository()
val userDeferred = repo.getUser()
// if onClean() is called before the coroutine in Repository finishes,
// this line will not be called, but coroutine in Repository will continue executing
val result = userDeferred.await() // wait for result of I/O operation without blocking the main thread
}
}
fun onClean() {
job.cancel()
}
}
class Repository {
fun getUser() = GlobalScope.async {
delay(4000)
// this line is executed no matter whether the job in UserViewModel was canceled or not
"User returned"
}
}
另外我们可以减少showUser()
函数:
fun showUser() = repo.getUser().then(this) {
// `it` contains the result
// here is the main thread, use `it` to update UI
}
使用扩展函数then
:
fun <T> Deferred<T>.then(scope: CoroutineScope = GlobalScope, uiFun: (T) -> Unit) {
scope.launch { uiFun(this@then.await()) }
}
如果您为 Android 开发并且想要确保即使在清理 ViewModel 之后您的 IO 操作也能完全执行,请使用 WorkManager。它适用于异步和可延迟的任务,这些任务需要保证即使应用程序退出,系统也会 运行 它们。
ViewModel 只能在配置更改后存活,一般情况下不会在 activity 破坏后存活。
如果您希望您的操作在 activity 破坏后继续,您应该使用生命周期超过 activity 的组件,即 Service。
此外,如果您想确保您的操作 "always" 已执行,您应该使用前台服务,该服务在 运行.
时需要有一个不可关闭的通知A started service can use the
startForeground(int, Notification)
API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory.