写Android应用时,很多人一开始都会用GlobalScope来启动协程,觉得方便省事。比如点击一个按钮去请求网络数据,顺手写上GlobalScope.launch,代码跑起来也没问题。可时间一长,项目变大后,这种写法带来的麻烦就慢慢浮现了。
GlobalScope到底是什么?
GlobalScope是Kotlin协程里的一个全局作用域,它不属于任何特定组件,一旦启动,协程就会在整个应用生命周期内运行,除非你手动取消。听起来好像挺方便,但正因为它的“全局性”,容易造成资源浪费甚至内存泄漏。
举个例子,你在Activity里用GlobalScope发了个网络请求:
GlobalScope.launch {
val data = fetchData()
withContext(Dispatchers.Main) {
textView.text = data
}
}
如果这个请求还没完成,用户就退出了页面,协程还在后台跑着。不仅浪费网络和CPU资源,还可能因为更新已经销毁的UI导致崩溃。
为什么说它像“野线程”?
你可以把GlobalScope比作在应用里随便开的“野线程”。没人管它什么时候结束,也不受页面生命周期控制。就像厨房里开了个火没关,哪怕人已经走了,锅还在烧。
更合适的做法是使用有明确生命周期的作用域,比如ViewModelScope或LifecycleScope。这些作用域会随着组件的销毁自动取消协程,避免资源浪费。
替代方案:用合适的Scope管理协程
在Activity或Fragment中,推荐使用lifecycleScope:
lifecycleScope.launch {
val result = async { fetchData() }.await()
updateUI(result)
}
这样只要页面销毁,协程也会自动取消。ViewModel里则可以用viewModelScope:
viewModelScope.launch {
repository.getData()
}
它会在ViewModel被清除时自动清理协程任务。
GlobalScope真的不能用吗?
也不是完全不能用,但在大多数场景下都不推荐。如果你确实需要一个全局后台任务,比如定时同步日志、上传埋点数据,那可以考虑自己创建一个有明确管理机制的单例协程作用域,而不是直接用GlobalScope。
比如:
object AppCoroutineScope : CoroutineScope {
override val coroutineContext: CoroutineContext
get() = SupervisorJob() + Dispatchers.Default
}
这样既能实现全局调度,又能统一管理和关闭。
开发过程中,别图一时省事,给后期埋坑。协程虽轻量,管理不当照样能把应用拖垮。