首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Android多线程与线程池从 Handler 到 Executor的实战演进

Android多线程与线程池从 Handler 到 Executor的实战演进

原创
作者头像
hunter android
发布2026-09-02 11:31:43
发布2026-09-02 11:31:43
470
举报

Android 多线程与线程池:从 Handler 到 Executor 的实战演进

Android 开发中,多线程是绕不开的话题。从最初的 Thread + Handler 到后来的 AsyncTask,再到如今的 Executor 和 Coroutines,线程管理方式一直在演进。本文从实际场景出发,梳理多线程在 Android 中的正确用法。

为什么需要多线程

Android 主线程(UI 线程)有严格的限制:

  • 不能执行耗时操作(网络请求、数据库查询、文件 I/O)
  • ANR 阈值:Activity 5 秒,BroadcastReceiver 10 秒
  • 只有主线程能更新 UI

违反这些规则会导致 ANR 或 CalledFromWrongThreadException

Handler + Looper:Android 的消息机制

Handler 是 Android 线程通信的基础。

基本用法

代码语言:javascript
复制
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 的常见陷阱

**内存泄漏**:非静态内部类 Handler 持有 Activity 引用。

代码语言:javascript
复制
// 错误写法

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) // 清理所有消息

    }

}

HandlerThread:带 Looper 的子线程

普通 Thread 没有 Looper,无法接收消息。HandlerThread 内置了 Looper。

代码语言:javascript
复制
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 开销大。线程池通过复用线程提升性能。

Executors 工具类

代码语言:javascript
复制
// 固定大小线程池

val fixedPool = Executors.newFixedThreadPool(4)



// 缓存线程池(自动扩容,适合大量短任务)

val cachedPool = Executors.newCachedThreadPool()



// 单线程池(保证任务顺序执行)

val singlePool = Executors.newSingleThreadExecutor()



// 调度线程池(延时和周期任务)

val scheduledPool = Executors.newScheduledThreadPool(2)

实战:并发下载多张图片

代码语言:javascript
复制
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()

    }

}

ThreadPoolExecutor:自定义线程池

Executors 内部都是调用 ThreadPoolExecutor,直接使用它能精确控制参数。

代码语言:javascript
复制
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:丢弃队列最老的任务

执行流程

  1. 提交任务
  2. 核心线程未满 → 创建核心线程执行
  3. 核心线程已满 → 任务入队
  4. 队列满 → 创建非核心线程(直到 maximumPoolSize)
  5. 线程数达到上限 → 触发拒绝策略

AsyncTask 已废弃

Android 11(API 30)正式废弃 AsyncTask,原因:

  • 内存泄漏隐患(持有 Activity 引用)
  • 生命周期管理混乱(Activity 销毁后任务继续)
  • 串行执行默认值(SERIAL\_EXECUTOR)容易被忽略

**替代方案**:Kotlin Coroutines + ViewModel + LiveData。

实战场景:分页加载 + 缓存

代码语言:javascript
复制
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)

            }

        }

    }

}

线程安全:避免并发问题

问题示例

代码语言:javascript
复制
class Counter {

    private var count = 0

    

    fun increment() {

        count++ // 非原子操作,并发时会丢失更新

    }

}

解决方案

**1. synchronized**

代码语言:javascript
复制
class Counter {

    private var count = 0

    

    @Synchronized

    fun increment() {

        count++

    }

}

**2. AtomicInteger**

代码语言:javascript
复制
class Counter {

    private val count = AtomicInteger(0)

    

    fun increment() {

        count.incrementAndGet()

    }

}

**3. ConcurrentHashMap**(替代 HashMap)

代码语言:javascript
复制
private val cache = ConcurrentHashMap<String, Data>()

最佳实践

  1. **避免裸 Thread**:用线程池管理
  2. **合理设置线程数**:
  • CPU 密集型:核心数 + 1
  • I/O 密集型:核心数 \* 2
  1. **及时释放**:Activity 销毁时清理 Handler 和线程池
  2. **命名线程**:方便日志追踪和 ANR 分析
  3. **优先用 Coroutines**:现代 Android 开发的首选

小结

  • Handler:线程通信基础,注意内存泄漏
  • HandlerThread:需要 Looper 的子线程场景
  • 线程池:复用线程,避免频繁创建
  • ThreadPoolExecutor:精确控制线程池行为
  • 线程安全:并发访问共享数据时用锁或原子类

多线程是 Android 性能优化的关键一环。掌握线程池和 Handler 的正确用法,能有效避免 ANR 和内存泄漏,为后续学习 Coroutines 打下基础。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • Android 多线程与线程池:从 Handler 到 Executor 的实战演进
    • 为什么需要多线程
    • Handler + Looper:Android 的消息机制
      • 基本用法
      • Handler 的常见陷阱
      • HandlerThread:带 Looper 的子线程
    • 线程池:复用线程,避免频繁创建
      • Executors 工具类
      • 实战:并发下载多张图片
      • ThreadPoolExecutor:自定义线程池
      • 执行流程
    • AsyncTask 已废弃
    • 实战场景:分页加载 + 缓存
    • 线程安全:避免并发问题
      • 问题示例
      • 解决方案
    • 最佳实践
    • 小结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档