Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/blackberry/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Kotlin 为什么需要使用viewModelScope.launch包装普通函数?_Kotlin_Kotlin Coroutines - Fatal编程技术网

Kotlin 为什么需要使用viewModelScope.launch包装普通函数?

Kotlin 为什么需要使用viewModelScope.launch包装普通函数?,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,以下代码来自 1:在我看来,一个suspend fun应该在另一个suspend fun或viewModelScope.launch{},with context{}中启动filterItems()只是一个普通函数,我不知道为什么需要用viewModelScope包装filterItems()。在函数filterTasks()中启动{},你能告诉我吗 2:在函数filterTasks()中,viewModelScope.launch{}将在协同程序中启动,它是异步的,我认为返回结果可能在我从vi

以下代码来自

1:在我看来,一个suspend fun应该在另一个suspend fun或
viewModelScope.launch{}
with context{}
中启动
filterItems()
只是一个普通函数,我不知道为什么需要用
viewModelScope包装
filterItems()
。在函数
filterTasks()
中启动{}
,你能告诉我吗

2:在函数
filterTasks()
中,
viewModelScope.launch{}
将在协同程序中启动,它是异步的,我认为
返回结果
可能在我从
viewModelScope.launch{}
获得
结果之前启动,所以
结果
可能为空,代码正确吗

代码

   private fun filterTasks(tasksResult: Result<List<Task>>): LiveData<List<Task>> {       
        val result = MutableLiveData<List<Task>>()

        if (tasksResult is Success) {
            isDataLoadingError.value = false
            viewModelScope.launch {
                result.value = filterItems(tasksResult.data, getSavedFilterType())
                //return filterItems(tasksResult.data, getSavedFilterType())  //It will cause error.
            }
        } else {
            result.value = emptyList()
            showSnackbarMessage(R.string.loading_tasks_error)
            isDataLoadingError.value = true
        }

        return result //I think it maybe be launched before I get the result from viewModelScope.launch{}
    }


    private fun filterItems(tasks: List<Task>, filteringType: TasksFilterType): List<Task> {
        val tasksToShow = ArrayList<Task>()
        // We filter the tasks based on the requestType
        for (task in tasks) {
            when (filteringType) {
                ALL_TASKS -> tasksToShow.add(task)
                ACTIVE_TASKS -> if (task.isActive) {
                    tasksToShow.add(task)
                }
                COMPLETED_TASKS -> if (task.isCompleted) {
                    tasksToShow.add(task)
                }
            }
        }
        return tasksToShow
    }
private fun filtertask(tasksResult:Result):LiveData{
val result=MutableLiveData()
如果(任务结果为成功){
isDataLoadingError.value=false
viewModelScope.launch{
result.value=filterItems(tasksResult.data,getSavedFilterType())
//返回filterItems(tasksResult.data,getSavedFilterType())//这将导致错误。
}
}否则{
result.value=emptyList()
showSnackbarMessage(R.string.loading\u tasks\u错误)
isDataLoadingError.value=true
}
返回结果//我想在我从viewModelScope.launch获得结果之前可能会启动它{}
}
private fun filterItems(任务:列表,筛选类型:任务筛选类型):列表{
val tasksToShow=ArrayList()
//我们根据请求类型过滤任务
for(任务中的任务){
何时(筛选类型){
所有任务->任务显示.添加(任务)
活动任务->如果(task.isActive){
tasksToShow.add(任务)
}
已完成的任务->如果(task.isCompleted){
tasksToShow.add(任务)
}
}
}
返回任务显示
}

是的,你说得对。但是,如果您在lifecycleScope.launch{}或viewModelScope.launch{}中查找launch{}的实现,您将发现“”,它是“将在提供的作用域的上下文中调用的协同程序代码”被强制转换为挂起,因此在launch{}之间的任何代码块是挂起代码块。因此,在您的示例中,filterItems被强制转换为挂起在引擎盖下,并用viewModelScope.launch{}包装,以完成其繁重的任务,而不是在主线程中

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    // the below line is doing the magic
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

我同意代码看起来可疑,主要原因是它将
filteriems
协同路由启动到
main
dispatcher,基本上只是推迟了
filteriems
将在GUI线程上运行的时刻。如果
filteriems
需要很长时间才能完成,则会阻塞GUI;如果不需要很长时间,那么为什么首先要启动并发协同程序


此外,在体系结构层面上,我看不出有什么理由让函数返回
LiveData
,而你只需要
suspend fun
返回
List
它就不会返回,除非它执行一些繁重的工作并且你想把它移到后台线程,这里就是这样。在这里,作者只是想分离工作,以便可以首先使用空列表更新实时数据,然后使用过滤列表更新(需要大量计算),但忘记了在主线程之外进行更新

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    // the below line is doing the magic
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
在这种特殊情况下,作者可能忘记添加后台调度程序作为参数

viewModelScope.launch(Dispatchers.Default)
因此,在这个场景中,预期的行为没有实现,所以您可以看到这个“荒谬”的协同过程


我认为你可以通过一个补丁为这个项目做出贡献:)

在主线程中执行计算密集型任务没有那么好,他们应该使用
分派器。默认值
。但是无论如何,他们返回了结果(LiveData),并让另一个线程在结果可用时推送结果。而且他们可能使用了
(可变)状态流
而不是
LiveData
,项目可能已经过时(上次更新是在8个月前),所以他们很有可能使用了那些可能会匆忙完成工作而从不更新代码的东西。
getSavedFilterType()
a
suspend fun
?如果没有,那么您不需要启动协同程序来运行
filteriems(…)
,除非它会阻塞当前线程太长时间。此外,您不能
从已启动的协同程序中返回值:它只是在其启动的范围内运行的一个fire-and-forget操作。如果要从协同程序返回值,请使用
async
返回类型为
T
wait
Deferred
值。在这种情况下,viewModelScope将用作此启动函数的作用域,该函数在内部使用Dispatchers.Main.immediate,因此,即使块被包装在挂起函数中,它仍然在主线程中执行,从而阻塞应用程序并可能导致ANR。他们使用MVVM Live with Data Binding,这就是为什么他们公开LiveData对象上的项。他们只是在其上使用转换,以便在需要时更容易使用缓存/检索新数据。