【源码】康一康过时的→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并发八股文后学习巩固~


参考文献






相关文章
|
6月前
|
Java Android开发
Android系统 获取用户最后操作时间回调实现和原理分析
Android系统 获取用户最后操作时间回调实现和原理分析
184 0
|
Java 调度 Android开发
【源码】康一康过时的→AsyncTask(中)
AsyncTask 对很多老Android来说,是一个很有年代感的东西了,想当年毕业找工作,AsyncTask可是面试必问。 随着 EventBus、RxJava、Kotlin协程 等的出现,它渐渐淡出了我们的视野,面试八股文也少了它的身影,很多新晋的Android开发估计都没听过它。不禁感叹:技术更新迭代真快,学不动了!面试不问,但一些老旧项目中还有用到它,接盘维护难免要学下,索性花一丢丢时间过一下:废弃原因 + 核心原理 + 演进历史。
158 0
|
Java API Android开发
【源码】康一康过时的→AsyncTask(上)
AsyncTask 对很多老Android来说,是一个很有年代感的东西了,想当年毕业找工作,AsyncTask可是面试必问。 随着 EventBus、RxJava、Kotlin协程 等的出现,它渐渐淡出了我们的视野,面试八股文也少了它的身影,很多新晋的Android开发估计都没听过它。不禁感叹:技术更新迭代真快,学不动了!面试不问,但一些老旧项目中还有用到它,接盘维护难免要学下,索性花一丢丢时间过一下:废弃原因 + 核心原理 + 演进历史。
289 0
|
Android开发
【Android 逆向】ART 函数抽取加壳 ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )(二)
【Android 逆向】ART 函数抽取加壳 ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )(二)
341 0
|
安全 Android开发
【Android 逆向】ART 函数抽取加壳 ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )(一)
【Android 逆向】ART 函数抽取加壳 ( ART 下的函数抽取恢复时机 | 禁用 dex2oat 机制源码分析 )(一)
446 0
|
安全 Java Android开发
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | BaseDexClassLoader 构造函数 | DexPathList 构造函数及后续调用 )
【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | BaseDexClassLoader 构造函数 | DexPathList 构造函数及后续调用 )
255 0
|
Ubuntu Linux 开发工具
Android系统源码学习步骤
  Android系统是基于Linux内核来开发的,在分析它在运行时库层的源代码时,我们会经常碰到诸如管道(pipe)、套接字(socket)和虚拟文件系统(VFS)等知识。   此外,Android系统还在Linux内核中增加了一些专用的驱动程序,例如用于日志系统的Logger驱动程序、用于进程间通信的Binder驱动程序和用于辅助内存管理的匿名共享内存Ashmem驱动程序。
1393 0
|
Android开发
Android项目实战(十一):moveTaskToBack(boolean ) 方法的使用
原文:Android项目实战(十一):moveTaskToBack(boolean ) 方法的使用 当你开发的程序被按后退键退出的时候, 你肯定不想让他就这么被finish()吧,那么就想把程序退置到后台就可。
1116 0
|
Android开发
Android应用开发—setResult()的调用时机
本文转载自setResult()的调用时机,此处做了重新的排版,只是感觉markdown的排版比较好看些,侵删。 今天遇到这样一个问题,我在Activity-A中用startActivityForResult()方法启动了Activity-B,并且在B中通过setResult()方法给A返回值,由于某些原因不能在setResult()之后立刻调用finish()函数,只能通过用户按Back键自己退出到A。
1656 0