7

我有一个用例,我想使用协程,但有点困惑如何实现它。

具有范围并绑定到 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 协程以避免内存泄漏?

4

2 回答 2

4

根据GlobalScope我认为我们可以依赖使用全局 CoroutineScope 启动的协程的文档,它总是被执行。文档说:

全局作用域用于启动在整个应用程序生命周期内运行且不会过早取消的顶级协程。

我已经实现了一些测试代码,并且当在存储库中的协程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 开发并希望确保您的 IO 操作即使在清理 ViewModel 之后也能完全执行,请使用WorkManager。它适用于需要保证即使应用程序退出系统也会运行它们的异步和可延迟任务。

于 2018-12-06T21:27:40.537 回答
1

ViewModel 只能在配置更改中幸存下来,并且在一般活动的破坏中无法幸存。

If you want your operation to continue beyond the destruction of activity you should use a component with a lifecycle beyond that of activity i.e Service.

Furthermore if you want to make sure your operation is "always" executed you should use a Foreground service which will need to have an undismissable notification while the service is running.

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.

于 2018-12-06T22:14:24.707 回答