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()}// 自動切回主線程更新UIupdateUI(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 = 0fun 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 應用。