在 Kotlin 协程中,选择正确的 Scope 非常重要。本文将详细解释 GlobalScope 和 Application Scope(通常指 CoroutineScope)的区别和使用场景。
📌 1. GlobalScope
特点:
- 全局生命周期:跟随整个应用进程
- 不会自动取消:除非应用进程结束
- 谨慎使用:容易造成内存泄漏
使用示例:
kotlin
// ⚠️ 不推荐 - 容易造成内存泄漏
fun riskyMethod() {
GlobalScope.launch {
// 长时间运行的任务
delay(5000)
updateUI() // 可能已经销毁的 Activity 中调用
}
}
// 需要手动管理
var job: Job? = null
fun startTask() {
job = GlobalScope.launch {
// 执行任务
}
}
fun stopTask() {
job?.cancel()
}
📌 2. Application Scope(推荐)
创建方式:
kotlin
// 方式1:自定义 Application Scope
class MyApplication : Application() {
// 自定义 Application Scope
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
override fun onCreate() {
super.onCreate()
// 初始化
}
override fun onTerminate() {
applicationScope.cancel()
super.onTerminate()
}
}
// 方式2:使用 AndroidX 的 Lifecycle
class MyApplication : Application() {
val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
}
📌 3. ViewModel Scope
在 Android 中更推荐使用 ViewModel 的 scope:
kotlin
class MyViewModel : ViewModel() {
// ViewModel 自带 viewModelScope
fun fetchData() {
viewModelScope.launch {
// 自动跟随 ViewModel 生命周期
val data = repository.getData()
_uiState.value = data
}
}
// 或者创建自定义 scope
private val customScope = CoroutineScope(
SupervisorJob() + Dispatchers.IO
)
override fun onCleared() {
super.onCleared()
customScope.cancel()
}
}
📌 4. Lifecycle Scope
kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// lifecycleScope 自动跟随生命周期
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// 只在 STARTED 状态执行
collectData()
}
}
// 或者使用 launchWhenStarted
lifecycleScope.launchWhenStarted {
// 当 Activity 进入 STARTED 状态时恢复
}
}
}
📌 5. 对比表格
| 特性 | GlobalScope | Application Scope | ViewModel Scope | Lifecycle Scope |
|---|---|---|---|---|
| 生命周期 | 应用进程 | 自定义控制 | ViewModel 生命周期 | Activity/Fragment 生命周期 |
| 自动取消 | ❌ 否 | ✅ 可自定义 | ✅ 是 | ✅ 是 |
| 推荐使用 | ❌ 很少 | ✅ 后台任务 | ✅ ViewModel 中 | ✅ UI 相关 |
| 内存安全 | ❌ 危险 | ✅ 可控 | ✅ 安全 | ✅ 安全 |
📌 6. 最佳实践
✅ 推荐做法:
kotlin
// 1. 在 Application 中创建全局 scope
class MyApp : Application() {
val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
companion object {
lateinit var instance: MyApp
private set
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
// 2. 使用 ViewModel scope(首选)
class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {
fun loadUser(userId: String) {
viewModelScope.launch {
_loading.value = true
try {
val user = userRepository.getUser(userId)
_user.value = user
} catch (e: Exception) {
_error.value = e.message
} finally {
_loading.value = false
}
}
}
}
// 3. 需要全局后台任务时
class AnalyticsService(context: Context) {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
fun logEvent(event: String) {
scope.launch {
// 发送分析事件到服务器
sendToServer(event)
}
}
fun cleanup() {
scope.cancel()
}
}
📌 7. 总结建议
1). 几乎不要使用 GlobalScope,除非:
- 编写测试代码
- 编写一次性脚本
- 明确知道任务需要一直运行到应用结束
2). 按层次选择 Scope:
- UI 更新 → lifecycleScope 或 viewModelScope
- 业务逻辑 → viewModelScope
- 全局后台任务 → Application Scope
- 数据库/网络 → IO Dispatcher + 合适的 Scope
3).遵循原则:
- 生命周期感知:协程应该能被正确取消
- 结构化并发:使用父-子协程关系
- 异常处理 :使用
SupervisorJob防止一个协程失败影响其他
协程创建的成本很低,但内存泄漏的成本很高。选择合适的 Scope 是编写健壮协程代码的关键。