简书链接:AsyncTask源码原理简要分析与总结 文章字数:2183,阅读全文大约需要8分钟 我这里采用的是最新的26的源码进行分析 简要总结 1、默认是串行执行任务 2、内部机制是当任务执行完毕后再次调用静态类执行器取出新的任务进行执行 3、内部队列容器存储是用ArrayDeque实现的。 4、更新进度是通过AsyncTask类静态字段静态handler类进行使用MESSAGE_POST_PROGRESS = 0x2;+obj AsyncTaskResult进行发送 5、doInBackground方法是在 6、内部使用了FeatureTask 7、内部使用了原子锁 用空间换时间 8、内部的任务执行是通过线程池进行操作,据说不同android版本设置的总数不同 8、一个AsyncTask对象不可以执行多次,任务执行完成扫尾打字流程是通过Callable->的call()执行完毕调用postResult发送FINISH 的Handler消息然后再在handlerMessage方法也就是主线程执行->finish()方法, 然后检查是取消的时候就回调onCancelled 否则调用onPostExecute() 最后修改状态为 Status.FINISHED 标记一个任务的报废和不可重用。当然标记为 RUNNING 也是无法继续执行第二次的
验证总结
1、默认是串行执行任务 执行入口
1 2 3 public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
sDefaultExecutor相关代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); 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); } } }
2、SerialExecutor 是静态的,如果不通过反射修改默认的串行调度器,的一般情况下回多次任务创建的时候通过会offer加入到队列中接着 调用 scheduleNext执行队列里面的任务, 队列是先进先出,poll()方法就是把最先放进去的取出来并从集合中移除, 上面的if(mActivity==nulll)```表示第一次的执行,这个执行将会把队列中的数据全部处理掉,知道没有的时候就结束, THREAD_POOL_EXECUTOR 是线程池,具体请看第7,
3、这种原理和我做的礼物动画排队差不多,但是我写的显然没有人家高深,android实现方式不是用hashmap存储任务 而是用ArrayDeque ,翻译过来叫双端队列参考下面的地址https://baike.baidu.com/item/deque/849385
那么我就忍不住想说说这数据结构了, 那么我的理解是认为这无非就是一个集合,或者直接当做数组的 下标进行分层理解 可以从最后一个头部进行poll()也可以从最底部那个进行poll 而poll如果要用ArrayList的理解代码就是 Object obj=remove(0); reutrn obj;和 Object obj=remove(getCount()-1); reutrn obj;当然,这玩意内部也是数组,数组,链表是都一种数据结构,为什么这么写ArrayList 有优势,因为他是专门处理队列用的,比较专业,效率自然高,基本上专门负责头部和尾部的插入的,所以ArrayList数组容器估计效率不如它了, 所以才有了ArrayDeque,这个类是java官方写的, 那么链表和数组啥区别? 而数组是不可扩展的,只能重建,链表就不同了,打个比方 ?比如Node的数据结构 我搭建一个结构实现节点和子节点的操作,那么就要封装一个 有parent和 childNodes字段, 那么这个精髓就大概可以无限扩展 也可以实现遍历, 而数组 当插入一个到中间的时候,也只能把中间位置以及之后的数据进行后移,所以才有了经典的问题, 查询更新快,实际上就是根据下标取是挺快的, 而不数组当达到了容量后为毛只能重新创建??? 至少java层只有复制 ,c代码层的话,估计也不行,从内存片段中理解,的确是连续的地址,但是后面有没有被别人用还真不知道呢!!
4、验证4、从源码handler 更新进度原理验证progresUpdate所处线程 首先采用懒加载方式而且是用同步锁锁起来了。
1 2 3 4 5 6 7 private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler;
内部InternalHandler源代码 从源码得出一个道理,他在handler里面手动调用了传递的任务模型里面的任务的onProgressUpdate方法,那么说明这个方法不是和runBackground所处同一个线程了,一般来说第一次调用AsyncTask初始化一定是在主线程,但是为了避免第一次在子线程调用,所以Handler传递了一个Looper.getMainLooper()保证第一次初始化一定是主线程,所以在这个onProgressUpdate被回调了,那么一定是在主线程的,一定是可以进行更新ui操作的。
publishProgress的执行逻辑
存放了当前```new```出来的task, 1 2 3 4 5 6 7 8 9 10 ``` private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
6、内部的FutureTask
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 mWorker = new WorkerRunnable<Params, Result>() { //实际上实现了Callable public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } }; mFuture = new FutureTask<Result>(mWorker) { // @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occurred while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
FutureTask的用法参考下面链接,作用就是得到执行结果,可取消。
7、原子锁相关讯息
这样声明是解决多线程并发的问题,自古以来这个都是非常头疼的问题,如果使用同步锁,那么会导致卡顿和等待,那么这种方式无疑是比较好的方式了,android的源码中大量的代码都是这么做的.如ThreadLocal类 在handler内部也是用到了,解决线程问题。 原子锁的作用参考这篇文章https://blog.csdn.net/zmx729618/article/details/52767736
8、THREAD_POOL_EXECUTOR_EXECUTE静态字段=ThreadPoolExecutor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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); } } } 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; }
从源码分析,发现用到这个字段的就2个地方,一个叫声明一个叫使用 也就是用来execute()包装的匿名runnnable 声明的代码
1 2 3 4 5 6 7 8 9 public static final Executor THREAD_POOL_EXECUTOR; 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;
第一个参数是线程池的容量根据返回可用的java虚拟机的处理器数 进行计算, 最大4个,最少的话如果处理器数量为小于2, ,那么线程池容量就变成了2,如果可用处理器输了超过了4,就设置为4。
1 2 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
保持30秒 写死的,
1 2 private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
最多允许128条汉子等待,传递的128代表这个等待的Runnable容量,阻塞的意思就是当任务没有执行完成的时候 那么下一个任务没法执行,那么这个任务就加入到BlockingQueue中最多127个,当等于这个容量的时候执行这句话 throw new IllegalStateException("Queue full");https://blog.csdn.net/javazejian/article/details/77410889?locationNum=1&fps=1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 /** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters and default rejected execution handler. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} is null */
1 2 3 4 5 6 7 *如果当前线程池中的线程数量大于核心线程池数量而且如果空闲线程空闲的时间大于 保活时间keepAliveTime的线程,则将会被终止。当线程池没有充分利用的情况下, 此策略可以减少资源的消耗。如果线程池之后,变得更活跃,则新的任务线程将会被创建。 我们可以用#setKeepAliveTime方法动态的改变保活时间,用一个 Long.MAX_VALU,TimeUnit#NANOSECONDS 作为保活时间,那么空闲的线程可以避免在线程池关闭之前被终止。 ’保活策略只有在当前线程池线程数量大于 核心线程池数量时,才起作用。#allowCoreThreadTimeOut用于控制当任务线程空闲时,是否允许线程等待 keepAliveTime时间,以便在这个过程中,有新的任务进来。
那么我认为从上面的的意思是安卓这边配置这个时间比较当,当 线程数量超过核心线程池总数量,那么已经那些空闲线程 大于30秒没有被使用的将被干掉,不知道翻译准确不准确,哈哈!。 英语不太好KEEP_ALIVE_SECONDS,安卓是设置30秒,而不是long最大值微妙
参考http://donald-draper.iteye.com/blog/2366934