Android之AsyncTask源码分析(第一篇:类加载)

(注意:本文基于API 28的源码分析,不同的API的源码不同)

类加载时机

    第一次在主线程中创建AsyncTask子类对象时,AsyncTask类作为其父类,会优先加载到内存中,它持有的类变量、静态代码块会在AsyncTask类加载的初始化阶段,合并成一个<clinit>(类构造器),<clinit>方法会执行,接下来看看<clinit>中有哪些语句执行

注意:本文根据AsyncTask中静态成员的编写顺序叙述

 

LOG_TAG

    private static final String LOG_TAG = "AsyncTask";

AsyncTask类持有的一个常量,用于过滤日志

 

CPU_COUNT

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

AsyncTask类持有的一个常量,用于当前Android设备的CPU核心数(磊哥显然看过AsyncTask源码,代码都一样……

2、核心线程池的线程总数,注意不管CPU核心有多少,CORE_POOL_SIZE的最大值为4

    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

 

3、线程池的线程总数则是CPU核心数乘以2,而后再+1

    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

 

4、线程存活时间30s

    private static final int KEEP_ALIVE_SECONDS = 30;

 

5、ThreadFactory对象,用于创建Thread对象

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

ThreadFactory为一个interface、它内部只定义了一个newThread方法,该处通过匿名内部类创建,重写了newThread方法,在newThread方法内部通过new Thread(Runnable,  String)创建Thread对象,好处是为AsyncTask创建的每一个Thread对象,都起了名字“AsyncTask #” + 原子类持有的int值,确保线程名字的唯一性(ThreadFactory对象持有的一个AtomicInteger对象mCount)

 

6、基于链表的有界阻塞队列,最大持有128个Runnable,后面看看它会给哪个线程池使用

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

 

7、AsyncTask类持有的第一个线程池

    public static final Executor THREAD_POOL_EXECUTOR;

 

8、静态代码块,创建了一个线程池对象,由THREAD_POOL_EXECUTOR负责持有(见7号知识点)

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

首先创建一个ThreadPoolExecutor对象,由局部变量threadPoolExecutor暂时持有,看下ThreadPoolExecutor构造方法的传入参数

a、CORE_POOL_SIZE(见2号知识点) 代表核心线程数量

b、MAXIMUM_POOL_SIZE(见3号知识点)代表线程池内的线程最大数量

c、KEEP_ALIVE_SECONDS(见4号知识点)代表核心线程与非核心线程的存活时间(后面解释为何代表核心线程的存活时间)

d、TimeUnit.SECONDS 代表线程存活时间的单位,此处单位是秒

e、sPoolWorkQueue(见6号知识点)代表阻塞队列,它负责持有任务

f、sThreadFactory(见5号知识点)代表线程工厂,它负责创建线程

然后通过threadPoolExecutor的allowCoreThreadTimeOut方法,传入true,代表核心线程也超时(默认值是false,这样KEEP_ALIVE_SECONDS就代表核心线程与非核心线程的存活时间)

最终会由AsyncTask的类变量THREAD_POOL_EXECUTOR负责持有

 

9、创建一个SerialExecutor对象,由AsyncTask类的SERIAL_EXECUTOR持有,详细代码(见13号知识点)

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

 

10、AsyncTask类持有的两个常量,后面看看要做什么

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

 

11、AsyncTask类持有的sDefaultExecutor,默认指向的是SERIAL_EXECUTOR(见9号知识点)

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

 

12、AsyncTask类持有的一个Handler对象sHandler,Handler机制太重要,无处不见

    private static InternalHandler sHandler;

 

13、静态内部类

    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

a、SerialExecutor对象持有一个ArrayDeque对象mTasks,它是双端队列(即能做栈、又能做队列)、持有一个Runnable对象mActive

b、为重写的execute方法,增加了一个synchronized修饰,同一时刻,获得对象锁的线程才能执行该方法,目的是为了共享变量mTasks的插入元素,在同一个时刻只有一个元素插入,在execute方法内部做了两件事情,第一是调用了mTasks的offer方法,插入了一个Runnable对象,在该Runnable对象重写的run方法内部,首先调用传入的Runnable对象r的run方法,然后会一定会执行的scheduleNext()方法,第二是当mActivity为null,会执行scheduleNext()方法

c、scheduleNext方法,从ArrayDeque对象mTasks取出Runnable对象,交由mActive持有,然后会在成功获取到任务到情况下,将任务提交到一个线程池里(见7号知识点),由线程池中的线程负责执行任务

 

14、获取一个主线程的Handler对象,它的唯一调用处是AsyncTask对象初始化持有的mHandler位置处

    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

 

15、用于变更默认的Executor(hide修饰)

    public static void setDefaultExecutor(Executor exec) {
        sDefaultExecutor = exec;
    }

 

总结

a、AsyncTask利用了Java现有的线程池技术

b、AsyncTask类持有一个线程池、由AsyncTask类的THREAD_POOL_EXECUTOR负责持有(一个常量)

c、AsyncTask对象提交的任务是串行执行(默认实现),意味着同一时刻只有一个Runnable会在线程池中执行

e、如果想要同一时刻多个Runnable并行,就需要你调用executeOnExecutor方法,并传入一个自定义的Executor实现类

f、Executor接口定义了作为执行者的规范,作者巧妙的实现了一个具备Executor能力的实现类SerialExecutor和线程池ThreadPoolExecutor配合的如此默契,厉害!(注意:实现Executor接口的类,不能算做线程池类,只能说是具备Executor的能力)

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值