【源码】康一康过时的→AsyncTask(下)

简介: AsyncTask 对很多老Android来说,是一个很有年代感的东西了,想当年毕业找工作,AsyncTask可是面试必问。 随着 EventBus、RxJava、Kotlin协程 等的出现,它渐渐淡出了我们的视野,面试八股文也少了它的身影,很多新晋的Android开发估计都没听过它。不禁感叹:技术更新迭代真快,学不动了!面试不问,但一些老旧项目中还有用到它,接盘维护难免要学下,索性花一丢丢时间过一下:废弃原因 + 核心原理 + 演进历史。

0x4、演进历史


Android 1.6


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// ① 只用到一个线程池,并行执行任务,配置如下
// 核心线程池→5、线程数最大值→128,非核心线程空闲存活时长→10s
// 堵塞队列:LinkedBlockingQueue → 最大值10
// ② 消息类型有三种:
// MESSAGE_POST_RESULT、MESSAGE_POST_PROGRESS、MESSAGE_POST_CANCEL
// ③ 总结:5个核心线程 + 123个非核心线程 + 10个任务 → 最多138个任务,牛啊
// handler还需处理AsyncTask取消的消息,后续变成了一个标志位~


Android 3.2


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// ① 用到了两个线程池,串行执行任务,配置如下:
// 核心线程池-5、线程数最大值-128,非核心线程空闲存活时长-1s
// 堵塞队列:LinkedBlockingQueue → 最大值10
// ② 串行线程池同9.0
// ③ 消息类型少了取消,通过判断mFuture.isCancelled()来判断取消状态


Android 4.4


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// ① 同样是串行,配置不再粗暴写死,而是基于当前可用的处理器数量进行计算:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
// ② 堵塞队列上限变成128
// ③ 提供了串行变并行的setDefaultExecutor(),但被注解为@hide


Android 7.0


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// 动了下线程池配置,看注释
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
// ① 核心线程池改成了2到4之间,不再是粗暴的CPU_COUNT+1,避免极端情况下占用
// 所有CPU影响到其他后台线程的调度~
// ② 线程空闲时间也调整为30s
// ③ 调用allowCoreThreadTimeOut(true),空闲时间过长连核心线程也会回收


Android 8.0


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// 线程池没动,多了两个AsyncTask的构造方法,而且用了@hide注解
public AsyncTask(@Nullable Handler handler) {
    this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
}
// 这里猜测是为了扩展,支持外部传入一个Handler,做一些定制化处理,hide说明是想给系统自用的~


Android 10.0


上面的原理基于9.0讲解的,直接跳过讲10.0,详细源码:

core/java/android/os/AsyncTask.java


改动如下:


// ① 线程池策略,发生了变化:
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int BACKUP_POOL_SIZE = 5;
private static final int KEEP_ALIVE_SECONDS = 3;
public static final Executor THREAD_POOL_EXECUTOR;
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>(), sThreadFactory);
    threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
// ② 核心线程数→1、最大线程数→20、空闲线程存活时间→3s、堵塞队列变成了SynchronousQueue
// 无缓冲等待队列(不存储元素的堵塞队列,添加元素后等待其他线程去走后才能继续添加)
// ③ 没有采用ThreadPoolExecutor已有的拒绝策略,自定义了一种拒绝策略:
private static final RejectedExecutionHandler sRunOnSerialPolicy =
        new RejectedExecutionHandler() {
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
        // As a last ditch fallback, run it on an executor with an unbounded queue.
        // Create this executor lazily, hopefully almost never.
        synchronized (this) {
            if (sBackupExecutor == null) {
                sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
                sBackupExecutor = new ThreadPoolExecutor(
                        BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
                        TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
                sBackupExecutor.allowCoreThreadTimeOut(true);
            }
        }
        sBackupExecutor.execute(r);
    }
};
// 定义了一个核心池和最大核心池都为5的线程池,当拒绝策略处理任务,最多可以另外开5个线程
// 来执行任务,如果还不够的话,会添加到一个几乎无限大的LinkedBlockingQueue队列中


Android 11.0


详细源码:core/java/android/os/AsyncTask.java


改动如下:


// 为AsyncTask 添加 @Deprecated注解,一些描述注释


0x5、小结


有关AsyncTask最后的提交定格在2019.12.19,时代的车轮只会滚滚向前,而我们终将化为一粒尘埃。了解源码可以看到工程师对线程池策略的不断调整,很适合背完Java并发八股文后学习巩固~


参考文献






目录
打赏
0
0
0
0
7
分享
相关文章
CMMI-决策分析与解决(DAR)
CMMI-决策分析与解决(DAR)
322 0
Android 最新实现沉浸式状态栏的效果
Android 最新实现沉浸式状态栏的效果
297 0
|
6月前
|
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
app开发之安卓Android+苹果ios打包所有权限对应解释列表【长期更新】-以及默认打包自动添加权限列表和简化后的基本打包权限列表以uniapp为例-优雅草央千澈
440 11
Mysql和Oracle数据库死锁查看以及解决
【8月更文挑战第11天】本文介绍了解决MySQL与Oracle数据库死锁的方法。MySQL可通过`SHOW ENGINE INNODB STATUS`查看死锁详情,并自动回滚一个事务解除死锁;也可手动KILL事务。Oracle则通过查询V$LOCK与V$SESSION视图定位死锁,并用`ALTER SYSTEM KILL SESSION`命令终止相关会话。预防措施包括遵循ACID原则、优化索引及拆分大型事务。
692 3
解決Android报错:Could not initialize class org.codehaus.groovy.reflection.ReflectionCache
解決Android报错:Could not initialize class org.codehaus.groovy.reflection.ReflectionCache
284 1
Kotlin开发安卓app,在使用 MediaPlayer 播放 res/raw 中的音乐时遇到突然中断的问题,而 onErrorListener 没有接收到任何报错
在使用 Android MediaPlayer 播放 res/raw 中的音乐时遇到中断问题,可能的原因包括资源问题、媒体文件编码格式、生命周期管理和设备资源配置。要排查问题,检查音频文件是否正确包含,格式编码是否支持,MediaPlayer 是否正确管理及释放,以及设备是否有足够存储和配置。通过设置 onErrorListener 日志和确保在 onDestroy 中释放资源来调试。如果文件过大,考虑使用 AssetManager。遵循这些步骤可帮助诊断并解决播放中断的问题。
安卓多线程和并发处理:提高应用效率
【4月更文挑战第13天】本文探讨了安卓应用中多线程和并发处理的优化方法,包括使用Thread、AsyncTask、Loader、IntentService、JobScheduler、WorkManager以及线程池。此外,还介绍了RxJava和Kotlin协程作为异步编程工具。理解并恰当运用这些技术能提升应用效率,避免UI卡顿,确保良好用户体验。随着安卓技术发展,更高级的异步处理工具将助力开发者构建高性能应用。
439 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等