范围在协程中感到困惑

时间:2018-11-25 22:30:43

标签: kotlin kotlinx.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协程将被取消以避免一旦清除视图模型后内存泄漏?

2 个答案:

答案 0 :(得分:3)

根据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开发的,并且即使在清理ViewModel之后也要确保完全执行IO操作,请使用WorkManager。它旨在用于异步和可延迟任务,这些任务需要保证即使应用程序退出,系统也将运行它们。

答案 1 :(得分:1)

ViewModel仅能保留配置更改,而不能幸免于活动破坏。

如果您希望操作继续进行到破坏活动之外,则应使用生命周期超出生命周期的组件,即 Service

此外,如果要确保“始终”执行操作,则应使用Foreground服务,该服务在运行时需要具有不可撤销的通知。

  

已启动的服务可以使用startForeground(int, Notification) API将该服务置于前台状态,在该状态下,系统认为该服务是用户主动意识到的东西,因此在内存不足时不适合杀死它。