Skip to content

在 Android 开发中,多线程编程是处理耗时操作(如网络请求、数据库读写、复杂计算)的核心技术,目的是避免阻塞主线程(UI 线程),确保应用流畅响应。以下是 Android 多线程编程的详细解析,涵盖关键机制、最佳实践和 Kotlin 示例代码


1. Android 线程模型

  • 主线程(UI 线程):负责处理 UI 更新和用户交互。禁止在主线程执行耗时操作,否则会导致应用无响应(ANR)。
  • 工作线程(后台线程):用于执行耗时任务,需通过线程间通信将结果返回主线程更新 UI。

2. 多线程实现方式

(1) Thread + Handler(传统方式)

kotlin
// 创建工作线程执行任务
val backgroundThread = Thread {
    // 模拟耗时操作
    Thread.sleep(2000)
    val result = "任务完成"

    // 通过 Handler 发送结果到主线程
    Handler(Looper.getMainLooper()).post {
        textView.text = result
    }
}
backgroundThread.start()

缺点:手动管理线程生命周期复杂,易引发内存泄漏。


(2) AsyncTask(已弃用)

kotlin
@Deprecated("Use coroutines or other modern APIs instead.")
class MyAsyncTask : AsyncTask<Void, Void, String>() {
    override fun doInBackground(vararg params: Void?): String {
        // 后台执行
        return "结果"
    }

    override fun onPostExecute(result: String) {
        // 主线程更新 UI
        textView.text = result
    }
}

// 启动任务
MyAsyncTask().execute()

缺点:已弃用,无法处理配置变更(如屏幕旋转),容易内存泄漏。


(3) HandlerThread

结合 HandlerLooper 的专用线程,适合长时间运行的任务。

kotlin
val handlerThread = HandlerThread("MyHandlerThread").apply { start() }
val handler = Handler(handlerThread.looper)

handler.post {
    // 后台执行任务
    val result = doWork()
    
    // 发送结果到主线程
    runOnUiThread { textView.text = result }
}

(4) ThreadPoolExecutor

管理线程池,复用线程资源,适合高频短任务。

kotlin
val executor = Executors.newFixedThreadPool(4)
executor.execute {
    val result = doWork()
    runOnUiThread { textView.text = result }
}

// 关闭线程池(在 onDestroy 中调用)
executor.shutdown()

(5) Kotlin 协程(推荐)

协程是轻量级线程,通过挂起(suspend)机制简化异步代码,避免回调地狱。

添加依赖
gradle
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
示例代码
kotlin
// 在 ViewModel 或 LifecycleScope 中启动协程
class MyViewModel : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            // 在主线程启动协程
            val result = withContext(Dispatchers.IO) { 
                // 切换到 IO 线程执行耗时操作
                doWork()
            }
            // 自动切回主线程更新 UI
            textView.text = result
        }
    }

    private suspend fun doWork(): String {
        delay(2000) // 模拟耗时操作
        return "数据加载完成"
    }
}

优势

  • 结构化并发:自动取消关联任务。
  • 生命周期感知:与 ViewModelLifecycle 绑定,避免内存泄漏。
  • 简洁语法:用同步代码风格写异步逻辑。

(6) WorkManager(后台任务调度)

处理延迟、周期性或约束条件(如网络连接)的后台任务。

kotlin
// 定义 Worker
class MyWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        // 执行后台任务
        return Result.success()
    }
}

// 调度任务
val request = OneTimeWorkRequestBuilder<MyWorker>()
    .setConstraints(
        Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build()
    )
    .build()

WorkManager.getInstance(context).enqueue(request)

3. 线程间通信

(1) Handler(传统方式)

kotlin
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post { 
    textView.text = "更新 UI" 
}

(2) LiveData

结合 ViewModel 实现数据驱动 UI。

kotlin
class MyViewModel : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> get() = _data

    fun loadData() {
        viewModelScope.launch(Dispatchers.IO) {
            val result = fetchFromNetwork()
            _data.postValue(result) // 切换到主线程更新 LiveData
        }
    }
}

// 在 Activity/Fragment 中观察
viewModel.data.observe(this) { result ->
    textView.text = result
}

(3) Flow

协程中的数据流,适合连续数据更新。

kotlin
fun fetchData(): Flow<String> = flow {
    repeat(5) {
        emit("数据块 $it")
        delay(1000)
    }
}

// 收集数据
lifecycleScope.launch {
    fetchData()
        .flowOn(Dispatchers.IO) // 指定数据生产线程
        .collect { value ->
            textView.text = value // 在主线程消费数据
        }
}

4. 避免内存泄漏

  • 取消协程:通过 viewModelScopelifecycleScope 自动管理。
  • 弱引用:避免在后台线程持有 Activity/Fragment 的强引用。
kotlin
class MyActivity : AppCompatActivity() {
    private val weakActivity = WeakReference(this)

    fun startTask() {
        CoroutineScope(Dispatchers.IO).launch {
            val activity = weakActivity.get() ?: return@launch
            // 使用 activity 更新 UI
        }
    }
}

5. 最佳实践

  1. 主线程仅处理 UI 更新:所有耗时操作移至工作线程。
  2. 优先使用协程:替代 AsyncTaskRxJava,简化异步代码。
  3. 合理选择线程池:I/O 密集型用 Dispatchers.IO,计算密集型用 Dispatchers.Default
  4. 处理异常:使用 try/catchCoroutineExceptionHandler 捕获后台任务错误。
kotlin
val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
    Log.e("Coroutine", "Error: ${throwable.message}")
}

viewModelScope.launch(exceptionHandler) {
    // 可能抛出异常的操作
}

总结

机制适用场景优点缺点
Thread+Handler简单后台任务灵活手动管理复杂,易泄漏
协程所有异步场景(推荐)简洁、结构化并发、生命周期感知需学习新概念
WorkManager延迟/周期性后台任务系统调度、支持约束条件不适合即时任务
LiveData/Flow数据驱动 UI 更新生命周期感知、响应式需结合 ViewModel 使用

掌握多线程编程是 Android 高级开发的核心技能,合理选择工具能显著提升应用性能和用户体验。