kotlin 协程: GlobalScope 和 Application Scope 选择和使用 —— 新手指南

在 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 是编写健壮协程代码的关键。

相关推荐
霸王大陆1 小时前
《零基础学 PHP:从入门到实战》教程-模块四:数组与函数-2
android·开发语言·php
v***43171 小时前
springboot3整合knife4j详细版,包会!(不带swagger2玩)
android·前端·后端
h***04772 小时前
MySQL 的 INSERT(插入数据)详解
android·数据库·mysql
w***4242 小时前
SpringSecurity的配置
android·前端·后端
s***35302 小时前
SpringMVC新版本踩坑[已解决]
android·前端·后端
不会写代码的猴子2 小时前
Android16重磅更新:安全与性能全面升级
android
霸王大陆2 小时前
《零基础学 PHP:从入门到实战》教程-模块四:数组与函数-1
android·开发语言·php
e***19353 小时前
MySQL-触发器(TRIGGER)
android·数据库·mysql
不会写代码的猴子3 小时前
Android16
android