首页 > 解决方案 > Kotlin 中 coroutineScope 的生命周期是多少?

问题描述

代码 A 来自https://github.com/android/architecture-samples上的项目架构示例

1:我不知道函数activateTask(task: Task)是否需要runBlocking像代码B一样包装。如果对象被快速销毁,恐怕activateTask(task: Task)可能无法运行DefaultTasksRepository

2:正常运行协程ViewModel.viewModelScope,不知道ViewModel.viewModelScopeapp完成后会不会被销毁,运行的协程是否ViewModel.viewModelScope也会被销毁。如果是这样,我认为会很糟糕,一些长时间的协程,例如向远程服务器写入数据将被取消。

3:还有,activateTask代码A中的函数是协程函数,可以直接调用另一个协程函数,所以我认为代码A+是正确的,对吧?

代码 A

import kotlinx.coroutines.coroutineScope
...

class DefaultTasksRepository(
    private val tasksRemoteDataSource: TasksDataSource,
    private val tasksLocalDataSource: TasksDataSource,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {
   ...

   override suspend fun activateTask(task: Task) = withContext<Unit>(ioDispatcher) {
        coroutineScope {
            launch { tasksRemoteDataSource.activateTask(task) }
            launch { tasksLocalDataSource.activateTask(task) }
        }
    }

   override suspend fun clearCompletedTasks() {
        coroutineScope {
            launch { tasksRemoteDataSource.clearCompletedTasks() }
            launch { tasksLocalDataSource.clearCompletedTasks() }
        }
    }
   ...
}

代码 A+

import kotlinx.coroutines.coroutineScope
...

class DefaultTasksRepository(
    private val tasksRemoteDataSource: TasksDataSource,
    private val tasksLocalDataSource: TasksDataSource,
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) : TasksRepository {
   ...

   override suspend fun activateTask(task: Task) = withContext<Unit>(ioDispatcher) {
        tasksRemoteDataSource.activateTask(task) 
        tasksLocalDataSource.activateTask(task)         
    }

   override suspend fun clearCompletedTasks() {            
        tasksRemoteDataSource.clearCompletedTasks() 
        tasksLocalDataSource.clearCompletedTasks()             
   }
   ...
}

代码 B

fun main() = runBlocking { 
    launch { 
      delay(1000L)
      println("World!")
    }
    println("Hello,")
}

标签: androidkotlinkotlin-coroutines

解决方案


  1. 您不应该在任何协程应用程序中使用 runBlocking,它会阻塞线程。

    如果您真的想使activateTask不可取消,那么标准库中已经有NonCancellable的工厂实现

    并且您不应该在 withContext 中使用 coroutineScope 包装器,因为新创建的 CoroutineScope 和新作业已经作为接收器在withContext.

    像这样实现您的 activateTask:

override suspend fun activateTask(task: Task) = withContext<Unit>(ioDispatcher + NonCancellable) {
    launch { tasksRemoteDataSource.activateTask(task) }
    launch { tasksLocalDataSource.activateTask(task) }
}

通过这种方式,它将在 IODispatcher 上调用,但不可取消,因为Job结果上下文的元素不提供取消它的功能。

  1. ViewModelScope 一直运行到您的应用程序被销毁,更多信息和生命周期图表在这里。如果您想运行一些非常重要的任务,请使用其他调度程序。

  2. 是的,代码 A+ 完全正确

PS:你不应该在协程应用程序中实现runBlocking,它的默认实现只是事件循环。

runBlocking 是桥接同步和异步代码的方式

更好的 main 函数实现应该是:

suspend fun main() = coroutineScope {
    // code here
}

它在 CommonPool 上运行,如果它挂起另一个协程可以重用同一个线程。


推荐阅读