Android 开发中,多线程是绕不开的话题。从最初的 Thread + Handler 到后来的 AsyncTask,再到如今的 Executor 和 Coroutines,线程管理方式一直在演进。本文从实际场景出发,梳理多线程在 Android 中的正确用法。
Android 主线程(UI 线程)有严格的限制:
违反这些规则会导致 ANR 或 CalledFromWrongThreadException。
Handler 是 Android 线程通信的基础。
class MainActivity : AppCompatActivity() {
private val handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 子线程执行耗时操作
Thread {
val result = fetchDataFromNetwork()
// 切回主线程更新 UI
handler.post {
textView.text = result
}
}.start()
}
}**内存泄漏**:非静态内部类 Handler 持有 Activity 引用。
// 错误写法
class MainActivity : AppCompatActivity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// 持有 Activity 引用,Activity 销毁后消息未清理会泄漏
}
}
}
// 正确写法:静态 Handler + 弱引用
class MainActivity : AppCompatActivity() {
private val handler = MyHandler(this)
private class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
private val weakRef = WeakReference(activity)
override fun handleMessage(msg: Message) {
weakRef.get()?.apply {
// 更新 UI
}
}
}
override fun onDestroy() {
super.onDestroy()
handler.removeCallbacksAndMessages(null) // 清理所有消息
}
}普通 Thread 没有 Looper,无法接收消息。HandlerThread 内置了 Looper。
class DataProcessor {
private val handlerThread = HandlerThread("data-processor")
private lateinit var handler: Handler
fun start() {
handlerThread.start()
handler = Handler(handlerThread.looper)
}
fun processData(data: String) {
handler.post {
// 在 data-processor 线程执行
val result = heavyComputation(data)
Log.d("Thread", Thread.currentThread().name) // data-processor
}
}
fun stop() {
handlerThread.quitSafely()
}
}频繁创建 Thread 开销大。线程池通过复用线程提升性能。
// 固定大小线程池
val fixedPool = Executors.newFixedThreadPool(4)
// 缓存线程池(自动扩容,适合大量短任务)
val cachedPool = Executors.newCachedThreadPool()
// 单线程池(保证任务顺序执行)
val singlePool = Executors.newSingleThreadExecutor()
// 调度线程池(延时和周期任务)
val scheduledPool = Executors.newScheduledThreadPool(2)class ImageDownloader {
private val executor = Executors.newFixedThreadPool(3)
fun downloadImages(urls: List, callback: (List) -> Unit) {
val results = mutableListOf<Bitmap?>()
val latch = CountDownLatch(urls.size)
urls.forEachIndexed { index, url ->
executor.execute {
try {
val bitmap = downloadBitmap(url)
synchronized(results) {
results.add(bitmap)
}
} catch (e: Exception) {
results.add(null)
} finally {
latch.countDown()
}
}
}
// 等待所有任务完成
Thread {
latch.await()
Handler(Looper.getMainLooper()).post {
callback(results.filterNotNull())
}
}.start()
}
fun shutdown() {
executor.shutdown()
}
}Executors 内部都是调用 ThreadPoolExecutor,直接使用它能精确控制参数。
val threadPool = ThreadPoolExecutor(
2, // corePoolSize:核心线程数
4, // maximumPoolSize:最大线程数
60L, // keepAliveTime:空闲线程存活时间
TimeUnit.SECONDS,
LinkedBlockingQueue(100), // workQueue:任务队列
ThreadFactory { r ->
Thread(r, "custom-worker").apply {
isDaemon = false
}
},
ThreadPoolExecutor.CallerRunsPolicy() // rejectedExecutionHandler:拒绝策略
)**核心参数说明**:
corePoolSize:常驻线程数,即使空闲也不回收maximumPoolSize:最大线程数,队列满后才会创建新线程keepAliveTime:超出核心数的线程空闲后回收时间workQueue:任务队列,核心线程忙时任务先入队rejectedExecutionHandler:队列满且线程数达到上限时的策略AbortPolicy:抛异常(默认)CallerRunsPolicy:调用者线程执行DiscardPolicy:静默丢弃DiscardOldestPolicy:丢弃队列最老的任务Android 11(API 30)正式废弃 AsyncTask,原因:
SERIAL\_EXECUTOR)容易被忽略**替代方案**:Kotlin Coroutines + ViewModel + LiveData。
class DataRepository {
private val executor = Executors.newFixedThreadPool(2)
private val cache = ConcurrentHashMap<Int, List>()
fun loadPage(page: Int, callback: (List) -> Unit) {
executor.execute {
// 先查缓存
cache[page]?.let {
Handler(Looper.getMainLooper()).post { callback(it) }
return@execute
}
// 缓存未命中,请求网络
val data = fetchFromNetwork(page)
cache[page] = data
Handler(Looper.getMainLooper()).post {
callback(data)
}
}
}
}class Counter {
private var count = 0
fun increment() {
count++ // 非原子操作,并发时会丢失更新
}
}**1. synchronized**
class Counter {
private var count = 0
@Synchronized
fun increment() {
count++
}
}**2. AtomicInteger**
class Counter {
private val count = AtomicInteger(0)
fun increment() {
count.incrementAndGet()
}
}**3. ConcurrentHashMap**(替代 HashMap)
private val cache = ConcurrentHashMap<String, Data>()核心数 + 1核心数 \* 2多线程是 Android 性能优化的关键一环。掌握线程池和 Handler 的正确用法,能有效避免 ANR 和内存泄漏,为后续学习 Coroutines 打下基础。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。