Kotlin协程-延迟,它是如何工作的? [英] Kotlin coroutines - delay, how does it work?

查看:11
本文介绍了Kotlin协程-延迟,它是如何工作的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很习惯使用RX来处理并发,但在我目前的工作中,我们混合了AsyncTask、Executors+Handler、Thads和一些LiveData。现在我们正在考虑转向使用Kotlin Coroutine(实际上已经开始在代码库中的某些地方使用它)。

因此,我需要开始熟悉Coroutine,最好是利用我现有的并发工具知识来加快进程。

我尝试使用Google codelab来帮助他们,虽然这让我对它有了一些了解,但它也提出了许多未回答的问题,所以我尝试通过编写一些代码、调试和查看日志输出来弄脏自己的手。

根据我的理解,协程由两个主要构建块组成;挂起函数是您工作的地方,协程上下文是您执行挂起函数的地方,这样您就可以控制协程将在哪个调度程序上运行。

这里我有一些下面的代码,它们的行为与我预期的一样。我已经使用Dispatcher.Main设置了一个协同程序上下文。因此,不出所料,当我启动协程getResources时,由于Thread.sleep(5000)

,它最终阻塞了UI线程5秒钟
private const val TAG = "Coroutines"

class MainActivity : AppCompatActivity(), CoroutineScope {
    override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        log("onCreate", "launching coroutine")
        launch {
            val resource = getResource()
            log("onCreate", "resource fetched: $resource")
            findViewById<TextView>(R.id.textView).text = resource.toString()
        }
        log("onCreate", "coroutine launched")
    }

    private suspend fun getResource() : Int {
        log("getResource", "about to sleep for 5000ms")
        Thread.sleep(5000)
        log("getResource", "finished fetching resource")
        return 1
    }

    private fun log(methodName: String, toLog: String) {
        Log.d(TAG,"$methodName: $toLog: ${Thread.currentThread().name}")
    }
}

当我运行此代码时,我看到以下日志:

2020-05-28 11:42:44.364 9819-9819/? D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:42:44.376 9819-9819/? D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:42:44.469 9819-9819/? D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:42:49.471 9819-9819/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:42:49.472 9819-9819/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main

如您所见,所有日志都来自主线程,Thread.sleep(5000)前后有5秒的间隔。在这5秒的间隔内,UI线程被阻塞,我可以通过查看仿真器来确认这一点;它不会呈现任何UI,因为onCreate被阻塞。

现在,如果我更新getResources函数以使用挂起FUNdelay(5000)而不是使用Thread.sleep(5000),如下所示:

private suspend fun getResource() : Int {
    log("getResource", "about to sleep for 5000ms")
    delay(5000)
    log("getResource", "finished fetching resource")
    return 1
}

然后我最终看到的东西让我感到困惑。我知道delayThread.sleep不同,但因为我是在Dispatchers.Main支持的协程上下文中运行它,所以我希望看到与使用Thread.sleep相同的结果。

相反,我看到的是UI线程在发生5秒延迟时没有被阻止,日志如下:

2020-05-28 11:54:19.099 10038-10038/com.example.coroutines D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:54:19.111 10038-10038/com.example.coroutines D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:54:19.152 10038-10038/com.example.coroutines D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:54:24.167 10038-10038/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:54:24.168 10038-10038/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main

我可以看到,在这种情况下,用户界面线程未被阻止,因为在延迟期间呈现用户界面,然后在5秒后更新文本视图。

所以,我的问题是,在这种情况下,延迟如何不阻塞UI线程(即使我的暂停函数中的日志仍然表明该函数正在主线程上运行...)

推荐答案

将挂起函数视为使用接受回调的函数的一种方式,但不要求您将该回调传递给它。相反,回调代码就是挂起函数调用下的所有内容。

此代码:

lifecycleScope.launch {
    myTextView.text = "Starting"
    delay(1000L)
    myTextView.text = "Processing"
    delay(2000L)
    myTextView.text = "Done"
}

有点像:

myTextView.text = "Starting"
handler.postDelayed(1000L) {
    myTextView.text = "Processing"
    handler.postDelayed(2000L) {
        myTextView.text = "Done"
    }
}
决不能指望挂起函数会被阻止。如果他们这样做了,那么他们的组成就不正确了。挂起函数中的任何块代码都应该包装在它的背景中,如withContextsuspendCancellableCoroutine(这是较低级别的代码,因为它直接与协程延续一起工作)。

如果您尝试像这样编写挂起函数:

suspend fun myDelay(length: Long) {
    Thread.sleep(length)
}

您将收到"不适当的阻塞方法调用"的编译器警告。如果您将其推送到后台调度程序,则不会收到警告:

suspend fun myDelay(length: Long) = withContext(Dispatchers.IO) {
    Thread.sleep(length)
}

如果您尝试将其发送到Dispatchers.Main,您将再次收到警告,因为编译器认为主线程上的任何阻塞代码都是不正确的。

这应该会让您了解挂起函数应该如何操作,但请记住,编译器并不总是将方法调用识别为阻塞。

这篇关于Kotlin协程-延迟,它是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆