协同程序中的范围混淆

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.

我已经实现了一些测试代码,当 jobUserViewModel 中被取消时,存储库中的协程继续执行。这是带有我的评论的代码:

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.