我什么时候应该使我的正常功能暂停功能?

时间:2018-11-09 14:49:04

标签: kotlin kotlin-coroutines

使用kotlin coroutines的最新稳定版本,我试图使用它来实现我的应用程序功能之一。但是,我有点困惑。

基本上,我有一个函数可以对项目列表进行一些处理。大约需要700-1000毫秒。

fun processList(list : ArrayList<String>) : ArrayList<Info> {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

现在,我希望它不会阻塞主线程。因此,我将在启动块内启动此函数,以便它不会阻塞主线程。

coroutineScope.launch(Dispatchers.Main) {
    val result = processList(list)
}

那很好。

但是,我试图使该函数成为挂起函数,以确保该函数不会阻塞主线程。实际上,在函数内部没有其他任何协程启动。还尝试在单独的协程中处理每个列表项,然后将它们全部加入以使其实际使用子协程。但是该循环内部的块使用了同步方法调用。因此,使其异步是没有意义的-并行。所以我最终有了这样的暂停功能:

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    for (item in list) {
        // Process list item
        // Each list item takes about ~ 15-20ms
        result.add(info) // add processed info to result
    }

    return result  // return result
}

开始处只有一个suspend修饰符,方法块用coroutineScope { }包装。

这还重要吗?哪一个更好?我应该仅在使用协程的情况下才使函数挂起功能,还是应该将长时间运行的功能标记为挂起功能?

我很困惑。我已经看过最近有关协程的所有讨论,但不清楚这一点。

有人可以帮我理解吗?

更新:

所以我最终拥有这样的功能。只是为了确保从未在主线程上调用该函数。并且调用函数不必到处都记住需要在后台线程上调用它。通过这种方式,我可以使调用函数的事物变得抽象:只需执行所告诉的一切,我就不在乎您要在哪里处理事物。只需处理并给我结果即可。因此,该函数可以自行处理需要运行的位置,而不是在调用函数上运行。

suspend fun processList(list : ArrayList<String>) : ArrayList<Info> = coroutineScope {

    val result = mutableListOf<Info>()

    launch(Dispatchers.Default) {
        for (item in list) {
            // Process list item
            // Each list item takes about ~ 15-20ms
            result.add(info) // add processed info to result
        }
    }.join() // wait for the task to complete on the background thread

    return result  // return result
}

这是正确的方法吗?

2 个答案:

答案 0 :(得分:2)

您希望将CPU密集型计算的工作卸载到后台线程,以便不会阻塞GUI线程。您无需声明任何暂停函数即可实现此目的。这就是您需要的:

myActivity.launch {
    val processedList = withContext(Default) { processList(list) }
    ... use processedList, you're on the GUI thread here ...
}

上文假设您已将CoroutineScope接口正确地添加到活动中,如其documentation中所述。

一种更好的做法是将withContext推入processList的定义中,这样您就不会犯在主线程上运行它的错误。声明如下:

suspend fun processList(list: List<String>): List<Info> = withContext(Default) {
    list.map { it.toInfo() }
}

这假定您已将字符串到信息的逻辑放入

fun String.toInfo(): Info = // whatever it takes

答案 1 :(得分:2)

暂停函数是回调函数的糖。它允许您以线性方式编写带有回调的代码。如果您的函数内部没有回调调用,也没有对另一个挂起的功能的调用,那么使您的函数挂起没有任何意义。除非您希望在后台线程中卸载函数内部的工作(挂起的功能并不总是与后台线程有关),否则,在这种情况下,请使用effects和适当的调度程序。在这种情况下,您可以选择将函数包装在launch/async中,也可以使函数挂起并在其中使用launch/async