kotlin 是否必须使用returnwithContext(Dispatchers.IO)?

hvvq6cgz  于 2023-01-17  发布在  Kotlin
关注(0)|答案(3)|浏览(202)

我使用的是Firebase身份验证。在存储库中,我使用了以下函数:

override suspend fun signIn(): Result<Boolean> {
    return try {
        auth.signInAnonymously().await()
        Result.Success(true)
    } catch (ex: Exception) {
        Result.Failure(ex)
    }
}

此函数从ViewModel类中调用:

var response by mutableStateOf<Result<Boolean>>(Result.Success(false))
    private set

fun signIn() = viewModelScope.launch {
    response = repository.signIn()
}

它工作正常,但我被建议在ViewModel中使用:

fun signIn() = viewModelScope.launch(Dispatchers.IO) {
    response = repository.signIn()
}

要在存储库中添加Dispatchers.IO和:

override suspend fun signIn(): Result<Boolean> {
    return withContext(Dispatchers.IO) {
        try {
            auth.signInAnonymously().await()
            Result.Success(true)
        } catch (ex: Exception) {
            Result.Failure(ex)
        }
    }
}

withContext启动一个协程。我不明白为什么?我用的是Jetpack编写。

xfyts7mz

xfyts7mz1#

不管是谁建议修改你的代码都是错的。
一般的Kotlin协程约定是,挂起函数永远不需要在特定的调度器上被调用,特别是因为它们永远不能阻塞。如果需要,它们总是在内部委托给特定的调度器。(但也许作为一种优化,一个 private suspend函数可以避免为必须在主调度器上被调用的函数这样做。)
由于这是一个约定,来自Google、Android、Square等的所有库以及任何知道自己在做什么的人,都只有suspend函数可以从 * 任何 * 调度器调用。
这包括你在Firebase中使用的await()调用,因此,你的仓库的signIn函数已经非常好了,因为它不调用任何阻塞函数,并且它调用的suspend函数是一个正确的suspend函数,也不阻塞,所以它符合标准(它不阻塞)。
ViewModel中的函数也很好,不需要指定调度程序。

yqkkidmi

yqkkidmi2#

实际上,由于您已经从以Dispatchers.IO开头的协程调用signIn,因此不必使用return withContext(...)
因为您的存储库方法是suspend,所以它能够调用协程,而不需要像withContext这样的特殊块。

// This line tells to launch code on separate IO thread, to avoid UI freezing
// Since default viewModelScope.launch runs on Dispatchers.Main, which is
// also used for rendering
fun signIn() = viewModelScope.launch(Dispatchers.IO) { 
    response = repository.signIn()
}

在存储库中,您可以

// Since signIn was called on IO context from viewModel, it will also
// return on IO
override suspend fun signIn(): Result<Boolean> {
    return try {
        auth.signInAnonymously().await()
        Result.Success(true)
    } catch (ex: Exception) {
        Result.Failure(ex)
    }
}
fae0ux8s

fae0ux8s3#

我们有两种方法来启动协同异步和启动。
1.launch将用于在后台执行串行/序列任务。
1.async在我们期望返回一些结果并且还希望执行并行操作时使用。
同样的方式withContext只是编写异步的另一种方式,在这种方式下,用户不必编写await()。当使用withContext时,它会串行而不是并行地运行任务。因此,用户应该记住,当我们在后台有一个任务,并且想要返回该任务的结果时,我们应该使用withContext。
在您的情况下,可以按如下所示更改代码

fun signIn() = viewModelScope.launch(Dispatchers.IO) {
        val response = async {  repository.signIn()}.await()
    }

并删除with上下文

suspend fun signIn(): Result<Boolean> {
     return try {
         auth.signInAnonymously().await()
         Result.Success(true)
     } catch (ex: Exception) {
         Result.Failure(ex)
     }
}

如果您不想将return与withContext一起使用,还有一种方法

override suspend fun signIn() = {
    withContext(Dispatchers.IO) {
        try {
            auth.signInAnonymously().await()
            Result.Success(true)
        } catch (ex: Exception) {
            Result.Failure(ex)
        }
    }
}

简单地说,如果你希望任务有结果,那么你必须使用async或者withContext
希望我能解决你的问题或问题。

相关问题