Android 中的多线程编程全面解析
一、Android 线程模型基础
主线程(UI 线程)特性
- 唯一性:每个应用只有一个主线程
- 职责:处理 UI 操作和用户交互
- 限制:禁止在主线程执行耗时操作(超过5秒会导致 ANR)
- 检查方法:
Looper.getMainLooper().thread == Thread.currentThread()
工作线程使用原则
- 网络请求:必须在工作线程执行
- 文件IO:超过毫秒级的操作应放在工作线程
- 数据库操作:Room 默认提供异步支持,复杂查询仍需工作线程
- 计算密集型任务:如加密、图像处理等
二、Android 多线程方案演进
1. 基础方案
// 基本线程创建
Thread {
// 后台操作
runOnUiThread {
// 更新UI
}
}.start()
2. Executor 框架(推荐基础方案)
val executor = Executors.newFixedThreadPool(4)
executor.execute {
// 后台任务
}
3. Android 特有机制
// HandlerThread 示例
val handlerThread = HandlerThread("MyHandlerThread").apply { start() }
val handler = Handler(handlerThread.looper)
handler.post {
// 在后台线程执行
}
三、现代协程解决方案(Kotlin Coroutines)
协程核心概念
- 轻量级线程:数千协程可并行,开销远小于线程
- 结构化并发:自动取消和资源清理
- 挂起函数:用同步写法实现异步操作
基本使用
// ViewModel 中启动协程
viewModelScope.launch {
// 主线程执行
val data = withContext(Dispatchers.IO) {
// 切换到IO线程执行网络请求
fetchDataFromNetwork()
}
// 自动切回主线程更新UI
updateUI(data)
}
关键组件
组件 | 作用 | 典型使用场景 |
---|---|---|
Dispatchers.Main |
主线程 | 更新UI |
Dispatchers.IO |
IO密集型 | 网络/文件操作 |
Dispatchers.Default |
CPU密集型 | 复杂计算 |
viewModelScope |
自动取消作用域 | ViewModel中的协程 |
lifecycleScope |
生命周期作用域 | Activity/Fragment中的协程 |
四、线程间通信机制
1. Handler/Looper 传统方式
val mainHandler = Handler(Looper.getMainLooper())
Thread {
// 后台工作
mainHandler.post {
// 主线程更新UI
}
}.start()
2. LiveData 自动线程切换
liveData.postValue(data) // 可从任何线程调用
liveData.observe(this) { data ->
// 自动在主线程回调
}
3. Flow 响应式流
flow {
emit(fetchData()) // 在IO线程发射数据
}
.map { compute(it) } // 在Default线程转换
.flowOn(Dispatchers.Default)
.collect { data -> // 在主线程收集
updateUI(data)
}
五、高级并发模式
1. 并发任务处理
// 并行执行多个请求
val deferred1 = async { fetchData1() }
val deferred2 = async { fetchData2() }
val result = deferred1.await() + deferred2.await()
2. 互斥与同步
val mutex = Mutex()
var sharedData = 0
fun updateData() = runBlocking {
mutex.withLock {
sharedData++
}
}
3. 超时控制
withTimeoutOrNull(3000) {
fetchData() // 超过3秒自动取消
} ?: showTimeoutError()
六、性能优化与陷阱规避
最佳实践
线程池配置:
// 根据CPU核心数优化线程池 val cpuCount = Runtime.getRuntime().availableProcessors() val executor = ThreadPoolExecutor( cpuCount + 1, cpuCount * 2 + 1, 30L, TimeUnit.SECONDS, LinkedBlockingQueue() )
避免内存泄漏:
lifecycleScope.launch { lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { flow.collect { data -> // 只在界面可见时处理数据 } } }
ANR 预防:
- 将超过100ms的操作移出主线程
- 使用
StrictMode
检测违规操作
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder() .detectNetwork() .penaltyLog() .build())
常见陷阱
回调地狱:
// 错误示例 fetchUser { user -> fetchProfile(user.id) { profile -> updateUI(user, profile) // 嵌套难以维护 } } // 协程改造 viewModelScope.launch { val user = fetchUserSuspend() val profile = fetchProfileSuspend(user.id) updateUI(user, profile) }
过度创建线程:
- 避免直接
new Thread()
,应使用线程池 - 协程不应执行阻塞操作(应用
withContext
)
- 避免直接
并发修改异常:
// 错误示例 val list = mutableListOf<Int>() (1..1000).forEach { i -> thread { list.add(i) // 可能崩溃 } } // 正确方案 val concurrentList = Collections.synchronizedList(mutableListOf<Int>())
七、调试与监控工具
Android Studio Profiler
- 查看线程状态和CPU使用率
- 检测线程泄漏和阻塞
StrictMode
StrictMode.setVmPolicy(VmPolicy.Builder() .detectLeakedClosableObjects() .penaltyLog() .build())
协程调试
// 在调试模式下启用协程名称 System.setProperty("kotlinx.coroutines.debug", "on")
自定义线程监控
// 监控线程创建 val threadFactory = ThreadFactory { r -> Thread(r).also { thread -> Log.d("ThreadMonitor", "Thread created: ${thread.name}") } }
Android 多线程编程已经从原始的 Thread+Handler
模式发展为以协程为核心的现代并发模型。开发者应当根据项目需求选择合适方案,小型项目可使用 Executor
+ Handler
,中大型项目推荐全面采用协程。无论采用何种方案,都需要注意线程安全、生命周期管理和性能优化,才能构建出响应迅速且稳定的 Android 应用。