【问题标题】:Understanding coroutineScope了解 coroutineScope
【发布时间】:2020-06-27 10:01:47
【问题描述】:

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

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 将在调用者的相同范围和上下文中启动?

【问题讨论】:

    标签: android scope android-context coroutine kotlin-coroutines


    【解决方案1】:

    setUserData 的启动范围和上下文是什么?

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

    如果我在setUserData 函数之前添加suspend 键,Tasksawait 方法将生成一个警告说Inappropriate blocking method call。为什么?

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

    IntelliJ/Android Studio 不够聪明(而且可能不能)来精确确定您是否在不应该阻止的时候阻止。所以这可能被认为是一个错误,但 setUserData 可以在 Dispatchers.IO 之外调用,所以 IntelliJ 在这里有一点。

    这个实现有什么不同?

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

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

    coroutineScope 是否保证 setUserData 将在调用者的相同范围和上下文中启动?

    您已经获得此保证。你不需要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

    【讨论】:

      猜你喜欢
      • 2021-05-29
      • 2020-04-09
      • 2020-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      • 2021-06-13
      • 1970-01-01
      相关资源
      最近更新 更多