【问题标题】:Kotlin coroutine in lifecycleScope doesn't block main threadLifecycleScope 中的 Kotlin 协程不会阻塞主线程
【发布时间】:2020-05-31 12:40:01
【问题描述】:

我对 ViewModels 中的协程感到困惑。

我的问题很简单:为什么看起来下面的协程没有阻塞 UIThread?(协程运行时 UI 仍然流畅)

我的片段就在这里:

class FragmentSeePaths : Fragment(R.layout.fragment_see_paths),
        PathRecyclerAdapter.OnSetPathForWidgetListener {
    private val pathViewModel: PathViewModel by activityViewModels()
    private lateinit var binding: FragmentSeePathsBinding
    private lateinit var listener: OnAddLineRequestListener

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
    }

    private fun observeWidgetPath() {
        pathViewModel.getUserWidgetPath().observe(viewLifecycleOwner, Observer {
            if (it != null) {
                lifecycleScope.launch {
                    val times = pathViewModel.fetchBusTime(it)
                    updateUI(it, times)
                }
            }
        })
    }

这里是使用 fetchBusTime 方法拍摄的 ViewModel:

suspend fun fetchBusTime(path: Path): Pair<List<Time>?, List<Time>?> {
        Log.v("fetchBusTimeUI", Thread.currentThread().name) // Main

        // Some network requests made with Retrofit
        val timesResponseStartPoint: GinkoTimesResponse? = repository.getTimes(
                path.startingPoint.startName,
                path.line.lineId,
                path.isStartPointNaturalWay
        )

        val timesResponseEndPoint: GinkoTimesResponse? = repository.getTimes(
                path.endingPoint.endName,
                path.line.lineId,
                path.isStartPointNaturalWay
        )

        return timesResponseStartPoint to timesResponseEndPoint
}

【问题讨论】:

  • 您是否阅读过任何有关协程是什么的文档?挂起函数永远不会阻塞主线程(只要它们编写正确)。它们暂停协程的执行(在主线程的情况下,它可以释放它来执行 UI 所需的任何其他操作),直到它们返回。
  • 是的,我读过它。问题是,将协程放入 dipatcher.IO 或 Dispatcher.default 有什么意义?我认为视频让我意识到我的代码中发生了什么。在主线程中启动协程时,当 Retrofit 被调用时,Retrofit 会自动切换上下文。有意义吗?
  • 任何正确编写的挂起函数都会在运行阻塞代码时从主调度器切换。简单挂起函数的常用习惯用法是suspend fun x(param: Int) = withContext(Dispatchers.IO) { /* some blocking code */ }。不调用任何阻塞代码的挂起函数,仅调用其他挂起函数和非阻塞函数,不需要显式更改调度程序。

标签: android kotlin mvvm coroutine


【解决方案1】:

launch 允许我们在后台启动协程并在此期间继续工作。 Suspending 函数可以暂停当前协程的执行而不阻塞当前线程。我们可以在以下任何调度程序下启动协程。

  1. dipatcher.IO -> 网络操作
  2. dispatcher.Main -> 在主线程上
  3. dispatcher.Default -> 用于 CPU 密集型操作

为了详细解释,我从文档中举一个例子:-

fun main() { 
    GlobalScope.launch { // launch new coroutine in background and continue
        delay(1000L)
        println("World!")
    }
    println("Hello,") // main thread continues here immediately
    runBlocking {     // but this expression blocks the main thread
        delay(2000L)  // ... while we delay for 2 seconds to keep JVM alive
    } 
}

cmets 应该为自己说话。这将立即打印“Hello”,并添加“World!”一秒钟后。 这与您的代码相同,挂起函数fetchBusTime() 将在不阻塞线程的情况下执行,在此方法内完成操作后,它将执行updateUI(it, times)

更多详情请看这篇文章here

【讨论】:

    猜你喜欢
    • 2020-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-19
    • 2016-06-26
    相关资源
    最近更新 更多