了解协程作用域

Understanding coroutineScope

假设在 VM 中使用 viewModelScope 启动了一个新协程,并在通用存储库中调用了一个非挂起函数 class

UserViewModel

viewModelScope.launch(context = Dispatchers.IO) {

                _user.value?.id?.let { id ->
                    val dataSaved = userRepository.setUserData(id, newUser)

                }

}

UserRepository

fun setUserData(id: String, data: User): Boolean {
        return try {
            val saveTask = db.collection(COLLECTION).document(id).set(data)
            Tasks.await(saveTask)
            true
        } catch (e: Throwable) {
            false
        }
}
  1. 启动 setUserData 的范围和背景是什么?
  2. 如果我在 setUserData 函数之前添加 suspend 键,Tasksawait 方法将生成一个警告说 Inappropriate blocking method call。为什么?
  3. 这个实现有什么不同?
suspend fun setUserData(id: String, data: User): Boolean {
        return coroutineScope {
            return@coroutineScope try {
                val saveTask = db.collection(COLLECTION).document(id).set(data)
                Tasks.await(saveTask)
                true
            } catch (e: Throwable) {
                e.printStackTrace()
                Log.e(TAG, "${e.message}")
                false
            }
        }
  }
  1. coroutineScope 是否保证 setUserData 将在调用者的相同范围和上下文中启动?

What's the scope and context in which the setUserData is launched?

viewModelScope.launch(context = Dispatchers.IO) 来看,上下文看起来是这样的 CoroutineScope(SupervisorJob() + Dispatchers.IO)

If I add the suspend keywork ahead of the setUserData function, the await method of the Tasks will generate a warning saying Inappropriate blocking method call. Why?

在挂起函数中阻塞线程(使用 Tasks.await)是不好的做法,除非调度程序支持这一点。即 Dispatchers.IO.

IntelliJ/Android Studio 不够智能(而且可能不能),无法准确确定您是否在不该阻止的情况下进行阻止。所以这可以被认为是一个错误,但是 setUserData 可以在 Dispatchers.IO 之外调用,所以 IntelliJ 在这里有一点。

What's the difference with this implementation?

真的不多。它只是有一些不值得的额外开销。

附带说明一下,coroutineScope 不会像 withContext 那样更改直接 coroutineContext,但它会更改已启动协程的继承上下文。

Is coroutineScope a guarantee that setUserData will be launched within the same scope and context of the caller?

您已经获得此保证。你不需要 coroutineScope 来实现这个。

你可能想要的是这个。

suspend fun setUserData(id: String, data: User): Boolean {
    val saveTask = db.collection(COLLECTION).document(id).set(data)
    return withContext(Dispatchers.IO) {
        try {
            Tasks.await(saveTask)
            true
        } catch (e: Throwable) {
            e.printStackTrace()
            Log.e(TAG, "${e.message}")
            false
        }
    }
}

这样调用者就不必担心要使用什么调度程序或上下文。

你应该用的是这个

suspend fun setUserData(id: String, data: User): Boolean {
    val saveTask = db.collection(COLLECTION).document(id).set(data)
    return try {
        saveTask.await() // From `kotlinx-coroutines-play-services`
        true
    } catch (e: Throwable) {
        e.printStackTrace()
        Log.e(TAG, "${e.message}")
        false
    }
}

这样您就不需要调用 Dispatchers.IO 和阻塞更多线程。

供参考,https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-play-services/kotlinx.coroutines.tasks/com.google.android.gms.tasks.-task/await.html