MainScope 与 GlobalScope

Bit*_*EVS 8 android kotlin kotlin-coroutines

GlobalScope 和 MainScope 有什么区别?

//Accessing data from Room
GlobalScope.launch {
            v.tvStoreName.text = pfViewModel.getStoreName()
            pageDetails.pageNumber = currentPage
            pageDetails.pageSize = pageSize
            pfViewModel.getTransactions(pageDetails, toolbarBuilder?.getDate()!!)
        }
Run Code Online (Sandbox Code Playgroud)

GlobalScope 有时会犯一个很难重现的错误。

致命异常:android.view.ViewRootImpl$CalledFromWrongThreadException:只有创建视图层次结构的原始线程才能触摸其视图。

MainScope().launch {
            var storeName = ""
            withContext(Dispatchers.Default) {
                storeName = pfViewModel.getStoreName()
            }
            v.tvStoreName.text = storeName
        }
Run Code Online (Sandbox Code Playgroud)

Jof*_*rey 16

GlobalScope 和 MainScope 有什么区别?

MainScopeCoroutineScope默认情况下使用调度程序Dispatchers.Main,它绑定到主 UI 线程。

是在其协程上下文中没有调度程序的GlobalScopeCoroutineScope这意味着在此范围内启动的协程将使用Dispatchers.Default调度程序,该调度程序由线程池支持(大小根据您拥有的 CPU 核心数量而定)。

GlobalScope在其上下文中也没有,这Job意味着结构化并发不适用。其中启动的协程永远不会自动取消,因此需要手动控制。这就是为什么通常不鼓励使用它,除非您有非常具体的需求。

只有创建视图层次结构的原始线程才能触摸其视图。

当您尝试从主线程外部修改视图时,就会发生此错误,如果您从主线程中启动的协程执行此操作,就会发生这种情况GlobalScope(因为它由单独的线程池支持)。

在第二个代码片段中,您使用的是withContext(Dispatchers.Default),它仅使这部分代码在该线程池上运行,但其余部分在 UI 线程上运行。这就是为什么 UI 更新可以正常进行的原因。

请注意,Room 已经使用带有后台线程池的调度程序来进行查询,因此您不需要像这样手动切换上下文,您只需从 UI 线程调用它即可。

旁注:MainScope().launch { .. }像这样使用是一个坏主意,因为它遇到与GlobalScope. 要正确使用它,您需要将此范围提取到变量/属性中,以便您可以在适当的时候取消它。也就是说,使用现有范围更容易。Android已经在具有生命周期的 Activity 等组件中提供了随时可用的协程作用域lifecycle-runtime-ktx(请参阅库)。它被称为lifecycleScope. 您应该在此范围内启动协程,以便在活动被销毁时它们会自动取消。