Android 开发中的多线程技术全面解析
Android 开发中的多线程技术全面解析
在 Android 开发中,多线程是提升应用性能、保证 UI 流畅性的核心技术之一。本文将系统介绍 Android 多线程的常见方案、适用场景及最佳实践。
一、为什么需要多线程
Android 应用的主线程(Main Thread)也叫 UI 线程,负责处理界面绘制、事件分发等任务。如果在主线程中执行耗时操作(如网络请求、数据库读写、文件 I/O、复杂计算),会导致界面卡顿甚至触发 ANR(Application Not Responding)。
典型耗时操作:
- 网络请求(HTTP/Socket)
- 数据库操作(SQLite、Room)
- 文件读写(磁盘 I/O)
- 图片解码与处理
- 复杂的数据计算或排序
Android 系统规定:主线程中执行超过 5 秒 的操作会触发 ANR 弹窗;广播接收者中超过 10 秒 也会触发 ANR。
二、Android 多线程方案总览
| 方案 | 适用场景 | 特点 |
|---|---|---|
Thread + Handler |
简单的后台任务 | 灵活但样板代码多 |
AsyncTask(已废弃) |
简单的异步任务 | API 30 后废弃,不推荐 |
HandlerThread |
需要消息循环的后台线程 | 自带 Looper,方便处理消息 |
IntentService(已废弃) |
后台串行任务 | API 30 后废弃,用 WorkManager 替代 |
ThreadPoolExecutor |
大量并发任务 | 可控线程数,复用线程 |
RxJava |
复杂的异步数据流 | 强大的线程调度与变换能力 |
Kotlin Coroutines |
现代异步编程首选 | 轻量、简洁、结构化并发 |
WorkManager |
持久化后台任务 | 保证任务执行,兼容 Doze 模式 |
三、传统方案详解
3.1 Thread + Handler
最基础的多线程方式,通过 Handler 在子线程和主线程之间传递消息。
class MainActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {
val result = msg.obj as String
textView.text = result
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Thread {
// 模拟耗时操作
Thread.sleep(2000)
val result = "数据加载完成"
val message = Message.obtain()
message.what = 1
message.obj = result
handler.sendMessage(message)
}.start()
}
}
优点: 简单直接,容易理解。
缺点: 样板代码多,容易产生内存泄漏(匿名内部类持有外部引用),线程不可复用。
3.2 HandlerThread
HandlerThread 继承自 Thread,内部自带 Looper,适合需要持续处理消息的后台线程。
val handlerThread = HandlerThread("background-thread")
handlerThread.start()
val backgroundHandler = Handler(handlerThread.looper) { msg ->
when (msg.what) {
1 -> {
// 在后台线程处理任务
val data = performHeavyCalculation()
// 切回主线程更新 UI
runOnUiThread {
textView.text = data
}
}
}
true
}
// 发送消息
backgroundHandler.sendEmptyMessage(1)
// 销毁时清理
override fun onDestroy() {
super.onDestroy()
handlerThread.quitSafely()
}
3.3 线程池 ThreadPoolExecutor
对于大量并发任务,频繁创建销毁线程开销很大。线程池可以复用线程,控制并发数量,提供更好的性能。
class AppExecutors {
val diskIO: ExecutorService = Executors.newFixedThreadPool(2)
val networkIO: ExecutorService = Executors.newFixedThreadPool(3)
val mainThread: Executor = Executor { command ->
Handler(Looper.getMainLooper()).post(command)
}
companion object {
private const val THREAD_COUNT = 3
}
}
// 使用示例
val executors = AppExecutors()
executors.diskIO.execute {
val data = readFromDatabase()
executors.mainThread.execute {
textView.text = data
}
}
推荐配置:
val cpuCount = Runtime.getRuntime().availableProcessors()
val corePoolSize = cpuCount + 1
val maxPoolSize = cpuCount * 2 + 1
val keepAliveTime = 30L
val threadPoolExecutor = ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
LinkedBlockingQueue(128),
ThreadFactory { r ->
Thread(r, "custom-thread").apply {
isDaemon = false
priority = Thread.NORM_PRIORITY
}
},
ThreadPoolExecutor.CallerRunsPolicy()
)
四、现代方案:Kotlin 协程
4.1 协程基础
Kotlin 协程是 Android 官方推荐的异步编程方案。它以同步的代码风格实现异步逻辑,轻量且安全。
// 在 build.gradle 中引入
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
}
核心概念:
suspend:挂起函数,可暂停执行不阻塞线程CoroutineScope:协程作用域,管理协程生命周期Dispatcher:调度器,决定协程在哪个线程运行
常用调度器:
| 调度器 | 说明 |
|---|---|
Dispatchers.Main |
主线程,用于 UI 操作 |
Dispatchers.IO |
IO 线程池,用于网络/磁盘操作 |
Dispatchers.Default |
计算密集型任务 |
Dispatchers.Unconfined |
不指定线程(不推荐常规使用) |
4.2 在 Activity/Fragment 中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// launch 启动协程,Main 调度器
lifecycleScope.launch {
// 在主线程
showLoading()
// 切换到 IO 线程执行耗时操作
val data = withContext(Dispatchers.IO) {
fetchDataFromNetwork()
}
// 自动切回主线程
hideLoading()
textView.text = data
}
}
private suspend fun fetchDataFromNetwork(): String {
delay(2000) // 模拟网络延迟
return "Hello Coroutines"
}
}
4.3 在 ViewModel 中使用
class MyViewModel(private val repository: Repository) : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState
fun loadData() {
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val data = repository.getData()
_uiState.value = UiState.Success(data)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "未知错误")
}
}
}
}
sealed class UiState {
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
4.4 并发与结构化并发
并行执行多个任务:
suspend fun loadPageData(): PageData = coroutineScope {
val userDeferred = async(Dispatchers.IO) { fetchUser() }
val postsDeferred = async(Dispatchers.IO) { fetchPosts() }
val configDeferred = async(Dispatchers.IO) { fetchConfig() }
PageData(
user = userDeferred.await(),
posts = postsDeferred.await(),
config = configDeferred.await()
)
}
超时控制:
try {
val result = withTimeout(3000L) {
fetchDataFromNetwork()
}
} catch (e: TimeoutCancellationException) {
// 处理超时
}
五、WorkManager:持久化后台任务
WorkManager 适用于需要保证执行的后台任务,即使应用退出或设备重启也能继续执行。
dependencies {
implementation("androidx.work:work-runtime-ktx:2.8.1")
}
5.1 定义 Worker
class UploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
val filePath = inputData.getString("file_path") ?: return Result.failure()
uploadFile(filePath)
Result.success(workDataOf("result" to "上传成功"))
} catch (e: Exception) {
Result.retry()
}
}
}
5.2 调度任务
// 一次性任务
val uploadRequest = OneTimeWorkRequestBuilder<UploadWorker>()
.setInputData(workDataOf("file_path" to "/sdcard/file.txt"))
.setConstraints(
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(false)
.build()
)
.setBackoffCriteria(
BackoffPolicy.LINEAR,
10,
TimeUnit.SECONDS
)
.build()
WorkManager.getInstance(context).enqueue(uploadRequest)
// 周期性任务(最小间隔 15 分钟)
val periodicRequest = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"sync_work",
ExistingPeriodicWorkPolicy.KEEP,
periodicRequest
)
六、RxJava 方案
RxJava 提供强大的异步编程能力,适合复杂的数据流处理。
dependencies {
implementation("io.reactivex.rxjava3:rxjava:3.1.5")
implementation("io.reactivex.rxjava3:rxandroid:3.0.2")
}
Observable.fromCallable {
// IO 线程执行
fetchDataFromNetwork()
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe { showLoading() }
.doFinally { hideLoading() }
.subscribe(
{ result -> textView.text = result },
{ error -> showError(error.message) }
)
线程调度器:
| 调度器 | 说明 |
|---|---|
Schedulers.io() |
IO 操作 |
Schedulers.computation() |
计算密集型 |
Schedulers.newThread() |
每次新建线程 |
AndroidSchedulers.mainThread() |
Android 主线程 |
七、线程安全与同步
7.1 共享变量问题
多线程访问共享变量时,可能出现数据不一致。
// ❌ 非线程安全
var counter = 0
fun increment() {
counter++ // 非原子操作
}
7.2 解决方案
方案一:synchronized
var counter = 0
val lock = Any()
fun increment() {
synchronized(lock) {
counter++
}
}
方案二:Atomic 原子类
val counter = AtomicInteger(0)
fun increment() {
counter.incrementAndGet()
}
方案三:Kotlin 协程的 Mutex
val mutex = Mutex()
var counter = 0
suspend fun increment() {
mutex.withLock {
counter++
}
}
方案四:单线程化(Thread Confinement)
// 所有操作限制在单一线程
val singleThreadExecutor = Executors.newSingleThreadExecutor()
八、内存泄漏与防范
8.1 常见泄漏场景
// ❌ 匿名内部类持有 Activity 引用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Thread {
Thread.sleep(10000)
textView.text = "done" // Activity 可能已销毁
}.start()
}
}
8.2 防范措施
1. 使用生命周期感知型组件:
// 自动在生命周期结束时取消
lifecycleScope.launch {
val data = fetchData()
textView.text = data
}
2. 弱引用:
class SafeHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val activityRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
val activity = activityRef.get() ?: return
// 安全使用 activity
}
}
3. 及时清理:
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null)
handlerThread.quitSafely()
disposable.dispose() // RxJava
}
九、最佳实践总结
9.1 选择指南
任务是否需要持久化?
├── 是 → WorkManager
└── 否
└── 任务是否复杂/需要数据流变换?
├── 是 → RxJava 或 Flow
└── 否
└── 是否是简单一次性异步任务?
├── 是 → Kotlin 协程(推荐)
└── 否 → 线程池 + Handler
9.2 核心原则
- 永远不要在主线程执行耗时操作,包括网络、数据库、文件 I/O。
- 优先使用 Kotlin 协程,它是 Android 官方推荐的方案。
- 使用生命周期感知型作用域(
lifecycleScope、viewModelScope),避免内存泄漏。 - 合理选择调度器:IO 操作用
Dispatchers.IO,计算用Dispatchers.Default,UI 更新用Dispatchers.Main。 - 避免滥用全局 GlobalScope,它不会被自动取消。
- 线程池要合理配置,核心数根据 CPU 核数和任务类型决定。
- 注意线程安全,多线程访问共享数据时做好同步。
- 废弃 API 不要用:
AsyncTask、IntentService已废弃,迁移到协程或 WorkManager。
9.3 性能优化技巧
- 使用
Flow替代回调进行数据流处理 - 对高频更新使用
Flow.conflate()只保留最新值 - 合理使用
Dispatchers.Default.limitedParallelism(n)限制并发数 - 避免在协程中调用阻塞操作,用
Dispatchers.IO包装 - 使用
Channel或StateFlow进行协程间通信
十、完整示例:MVVM + 协程
// Repository
class UserRepository(private val api: UserApi, private val dao: UserDao) {
suspend fun getUser(id: String): User {
// 先读本地缓存
val cached = withContext(Dispatchers.IO) { dao.getUser(id) }
if (cached != null) {
// 异步刷新远程数据
launch(Dispatchers.IO) {
try {
val remote = api.getUser(id)
dao.insert(remote)
} catch (e: Exception) {
// 忽略刷新失败
}
}
return cached
}
// 无缓存,直接请求网络
return api.getUser(id).also { dao.insert(it) }
}
}
// ViewModel
class UserViewModel(private val repository: UserRepository) : ViewModel() {
private val _state = MutableStateFlow<UserState>(UserState.Idle)
val state: StateFlow<UserState> = _state
fun loadUser(id: String) {
viewModelScope.launch {
_state.value = UserState.Loading
_state.value = try {
UserState.Success(repository.getUser(id))
} catch (e: Exception) {
UserState.Error(e)
}
}
}
}
// Activity
class UserActivity : AppCompatActivity() {
private val viewModel: UserViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user)
lifecycleScope.launch {
viewModel.state.collect { state ->
when (state) {
is UserState.Idle -> Unit
is UserState.Loading -> showLoading()
is UserState.Success -> showUser(state.user)
is UserState.Error -> showError(state.error)
}
}
}
viewModel.loadUser("123")
}
}
结语
Android 多线程技术经历了从 Thread/Handler 到 AsyncTask,再到 RxJava,最终到 Kotlin 协程 的演进。协程以其简洁的语法、结构化并发和生命周期感知能力,成为当前最推荐的方案。但对于持久化后台任务,WorkManager 仍是不可替代的选择。
掌握多线程不仅是写出能跑的代码,更要关注线程安全、内存管理、生命周期这些关键问题。希望本文能帮助你在 Android 多线程开发中做出正确的技术选型。
- 点赞
- 收藏
- 关注作者
评论(0)