###

第2章:AsyncTask原理与局限:线程池、生命周期、内存泄漏、版本兼容写法

AsyncTask 是 Android 早期提供的一种轻量级异步任务封装,内部自动实现了线程池 + Handler 的回调机制。它在 API 1 引入,API 30 被正式标记为弃用。理解它的原理与局限,能帮助开发者厘清 Android 任务队列的演进方向,并为后续章节(如 Executor、Coroutine)打下基础。

2.1 AsyncTask 核心原理

AsyncTask 底层由三部分构成:

  • 线程池 (ThreadPoolExecutor):负责执行后台任务,不同 API 版本使用的线程池参数不同。
  • 内部 Handler (InternalHandler):将后台线程的结果/进度发送到主线程,从而能够安全更新 UI。
  • 生命周期绑定:AsyncTask 会持有 Activity/Fragment 引用,导致与组件生命周期耦合。

执行流程分三步:

  1. 主线程调用 execute(),AsyncTask 将任务提交到线程池。
  2. Worker 线程执行 doInBackground(),期间可调用 publishProgress() 回调主线程的 onProgressUpdate()。
  3. 任务结束后,主线程先后执行 onPostExecute()(成功)或 onCancelled()(取消)。
关键 API 调用差异:execute() 在 API 4+ 是串行执行(单线程池),executeOnExecutor() 可自定义并行或串行执行器(API 11+)。

2.2 线程池参数版本差异

API Level核心线程数最大线程数队列类型执行策略
1-311无(直接创建新线程)串行
4-105128LinkedBlockingQueue并行(容易 OOM)
11-30CPU核心数+1CPU核心数*2+1同步队列(SynchronousQueue)默认串行(可通过线程池并行)

自 API 11 起,默认执行器由 THREAD_POOL_EXECUTOR 切换为 SERIAL_EXECUTOR,同一时间内只有一个任务被执行,避免并行带来的资源耗尽问题。

2.3 生命周期与内存泄漏

AsyncTask 常见的两个生命周期问题:

  • 内存泄漏:AsyncTask 内部的非静态匿名内部类持有了外部 Activity 引用,若 Activity 已销毁而任务仍在运行,Activity 无法被 GC 回收。
  • 结果丢失:Activity 销毁重建后,旧的 AsyncTask 句柄失效,onPostExecute() 中更新 UI 会导致空指针或逻辑错误。
常见错误示例:直接使用非静态内部类或匿名类创建 AsyncTask,未在 onDestroy() 中调用 cancel()。

2.4 版本兼容写法(Demo)

传统写法(Android Studio 中会提示弃用):

// 内部类,容易泄漏
public class MyActivity extends AppCompatActivity {
    private class MyTask extends AsyncTask<Void, Void, String> {
        @Override
        protected String doInBackground(Void... voids) {
            // 耗时操作
            return "result";
        }
        @Override
        protected void onPostExecute(String result) {
            textView.setText(result); // 若 Activity 已销毁则崩溃
        }
    }
}

兼容性优化写法(避免泄漏+适配低版本):

public class MyActivity extends AppCompatActivity {
    private static class SafeTask extends AsyncTask<Void, Void, String> {
        private WeakReference<MyActivity> activityRef;

        SafeTask(MyActivity activity) {
            activityRef = new WeakReference<>(activity);
        }
        @Override
        protected String doInBackground(Void... voids) {
            return "result";
        }
        @Override
        protected void onPostExecute(String result) {
            MyActivity activity = activityRef.get();
            if (activity != null && !activity.isFinishing()) {
                activity.textView.setText(result);
            }
        }
    }

    // 使用默认串行执行器(API 11+ 兼容)
    new SafeTask(this).executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
}

2.5 调试方法

  • 使用 android.os.AsyncTask.DEBUG = true(API 4+)可打印线程池日志。
  • 通过 getStatus() 方法获取任务状态:PENDING、RUNNING、FINISHED。
  • 使用 Thread.currentThread().getName() 在 doInBackground 中确认线程名。

2.6 性能与稳定性优化

优化手段说明适用版本
使用弱引用包裹外部引用避免内存泄漏所有版本
及时取消并检查 isCancelled()防止浪费线程和中间更新所有版本
避免大对象在 Result 中传递减少主线程阻塞风险所有版本
使用 executeOnExecutor 自定义线程池控制并发度,防止 OOMAPI 11+
替代方案:ViewModel + LiveData / Coroutine彻底根除生命周期问题AndroidX
版本兼容检查:在 API 30+ 设备上,AsyncTask 虽然仍可使用,但 IDE 会提示弃用。建议新项目使用 Kotlin Coroutines 或 RxJava。
AsyncTask 执行流程与风险点 主线程启动 AsyncTask 线程池(Serial/Parallel 依赖版本) Worker 线程 doInBackground() onProgressUpdate() (主线程回调) onPostExecute() (主线程回调) 常见风险点(内存泄漏 & 结果丢失) • 非静态内部类持有 Activity 引用 → GC 无法回收 • Activity 销毁后 onPostExecute 更新 UI → 空指针 • 线程池并行(API 4-10)导致大量线程耗尽系统资源

2.7 常见错误小结

  • 内存泄漏:未使用静态内部类 + 弱引用。
  • 串行阻塞:低版本误用 execute() 导致多个任务排队。
  • 取消不当:仅调用 cancel() 而不在 doInBackground 中检查 isCancelled(),任务仍会执行完才结束。
  • 跨 Activity 重建:未使用 Loaders / ViewModel 保存任务句柄。

本课核心:AsyncTask 是理解线程+Handler模型的经典案例,但因其生命周期耦合、版本碎片化及性能风险,现代开发应尽快过渡到更安全的方案(Coroutine、RxJava 或 LiveData+Room)。第3章将分析 HandlerThread 的场景化应用。