AsyncTask源码分析
Android 线程简单分析(一)
Android 并发之synchronized锁住的是代码还是对象(二)
Android 并发之CountDownLatch、CyclicBarrier的简单应用(三)
Android 并发HashMap和ConcurrentHashMap的简单应用(四)(待发布)
Android 并发之Lock、ReadWriteLock和Condition的简单应用(五)
Android 并发之CAS(原子操作)简单介绍(六)
Android 并发Kotlin协程的重要性(七)(待发布)
Android 并发之AsyncTask原理分析(八)
Android 并发之Handler、Looper、MessageQueue和ThreadLocal消息机制原理分析(九)
Android 并发之HandlerThread和IntentService原理分析(十)
相信AsyncTask大家都不陌生。AsyncTask内部封装了线程池和Handler,AsyncTask允许我们在后台进行耗时操作且把结果及时更新到UI上。当然AsyncTask从最开始到现在已经经过了几次代码修改,任务的执行逻辑慢慢地发生了改变,并不是大家所想象的那样:AsyncTask是完全并行执行的就像多个线程一样,其实不是的,所以用AsyncTask的时候还是要注意,下面会一一说明。
AsyncTask到底是串行还是并行?
new MyAsyncTask("MyAsyncTask1").execute("");new MyAsyncTask("MyAsyncTask2").execute("");new MyAsyncTask("MyAsyncTask3").execute("");new MyAsyncTask("MyAsyncTask4").execute("");new MyAsyncTask("MyAsyncTask5").execute("");
看一下日志:
MyAsyncTask1 === 2019-05-03 07:35:23MyAsyncTask2 === 2019-05-03 07:35:26MyAsyncTask3 === 2019-05-03 07:35:29MyAsyncTask4 === 2019-05-03 07:35:32MyAsyncTask5 === 2019-05-03 07:35:35MyAsyncTask6 === 2019-05-03 07:35:38
从5个AsyncTask共耗时15s且时间间隔为3s,很显然是串行执行的。
既然下来我们来分析一下源码:
public abstract class AsyncTask { private static final String LOG_TAG = "AsyncTask"; // 获取当前CPU数量 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)); // 线程池最大容量 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; // 空闲线程存活时间 private static final int KEEP_ALIVE_SECONDS = 30; //ThreadFactory 线程工厂,通过工厂方法newThread来获取新线程 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()); }}; //阻塞式队列,用来存放待并发执行的任务,初始容量:128个 private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(128); /** * 并发线程池,可以用来并行执行任务,从3.0开始AsyncTask默认是串行执行任务 * 但是我们仍然能构造出并行的AsyncTask */ 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;} /** * 串行任务执行器,其内部实现了串行控制, * 循环的取出一个个任务交给上述的并发线程池去执行 */ public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //消息类型:发送结果 private static final int MESSAGE_POST_RESULT = 0x1; //消息类型:更新进度 private static final int MESSAGE_POST_PROGRESS = 0x2; /** * 静态Handler,用来发送上述两种通知,采用UI线程的Looper来处理消息, * 在Android 5.2之前,AsyncTask必须在UI线程调用,因为子线程 * 默认没有Looper无法创建下面的Handler,程序会直接Crash,但是在5.2之后InternalHandler内部通过 * sHandler = new InternalHandler(Looper.getMainLooper()),所以没必要在UI线程也是可以的 */ private static InternalHandler sHandler; private final Handler mHandler; /** * 默认任务执行器,被赋值为串行任务执行器,AsyncTask变成串行执行任务,SERIAL_EXECUTOR其内部通过 * try {r.run();} finally {scheduleNext();}实现了串行执行。 */ private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; // 这两个就是任务执行体 private final WorkerRunnable mWorker; private final FutureTask mFuture; //任务的状态 默认为等待状态,即等待执行,其类型标识为volatile线程可见 private volatile Status mStatus = Status.PENDING; //原子布尔型,支持高并发访问,标识任务是否被取消 private final AtomicBoolean mCancelled = new AtomicBoolean(); //原子布尔型,支持高并发访问,标识任务是否被执行过 private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); /** * 串行执行器的实现,我们要好好看看,它是怎么把并行转为串行的 * 目前我们需要知道,asyncTask.execute(Params ...)实际上会调用 * SerialExecutor的execute方法,这一点后面再说明。也就是说:当你的asyncTask执行的时候, * 首先你的task会被加入到任务队列,然后排队,一个个执行。 * 总结: * * 当任务来临时,首先就是入队列,然后判断当前有没有任务正在执行 * 如果没有任务执行则直接调用scheduleNext(),执行任务。 * 当当前任务执行完成又再次在finally块调用scheduleNext执行下一个任务 */ private static class SerialExecutor implements Executor { //线性双向队列,用来存储所有的AsyncTask任务 final ArrayDeque mTasks = new ArrayDeque(); //当前正在执行的AsyncTask任务 Runnable mActive; public synchronized void execute(final Runnable r) { //将新的AsyncTask任务加入到双向队列中 mTasks.offer(new Runnable() { public void run() { try { r.run();//执行AsyncTask任务 } finally { /** * 当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话 * 这一点很明显体现了AsyncTask是串行执行任务的,总是一个任务执行完毕才会执行下一个任务 */ scheduleNext(); } } }); //如果当前没有任务在执行,直接进入执行逻辑 if (mActive == null) { scheduleNext(); } } //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行 protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } } public enum Status { /** * 任务等待执行 */ PENDING, /** * 任务正在执行 */ RUNNING, /** * 任务已经执行结束 */ FINISHED, } /** * 在Android 5.2之前,AsyncTask必须在UI线程调用,因为子线程 * 默认没有Looper无法创建下面的Handler,程序会直接Crash,但是在5.2之后InternalHandler内部通过 * sHandler = new InternalHandler(Looper.getMainLooper()),所以没必要在UI线程也是可以的 */ private static Handler getMainHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(Looper.getMainLooper()); } return sHandler; } } private Handler getHandler() { return mHandler; } /** * @hide 设置默认执行器 */ public static void setDefaultExecutor(Executor exec) { sDefaultExecutor = exec; } /** * 创建新的任务,这个方法必须在UI线程中调用,其实在5.2之后工作线程也是可以的 */ public AsyncTask() { this((Looper) null); } /** * 创建新的任务,这个方法必须在UI线程中调用,其实在5.2之后工作线程也是可以的 * * @hide */ public AsyncTask(@Nullable Handler handler) { this(handler != null ? handler.getLooper() : null); } /** * 创建新的任务,这个方法必须在UI线程中调用,其实在5.2之后工作线程也是可以的 * * @hide */ public AsyncTask(@Nullable Looper callbackLooper) { /**构造Handler*/ mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); /**构造任务执行体*/ mWorker = new WorkerRunnable() { 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; } }; /**构造任务执行体,传入mWorker*/ mFuture = new FutureTask(mWorker) { @Override protected void done() { try { //执行Future,通过get获取结果 Result result = get(); postResultIfNotInvoked(result); } 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); } } }; } private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } } //doInBackground执行完毕,发送消息 private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result;} public final Status getStatus() { return mStatus; } /** * 这个方法是我们必须要重写的,用来做后台计算,使用指定参数通过execute方法执行任务。 * 这个方法能够调用publishProgress方法去更新进度。 * 所在线程:后台线程 */ @WorkerThread protected abstract Result doInBackground(Params... params); /** * 在doInBackground之前调用,用来做初始化工作 * 所在线程:UI线程 */ @MainThread protected void onPreExecute() { } /** * 在doInBackground之后调用,用来接受后台计算结果更新UI * 所在线程:UI线程 */ @SuppressWarnings({"UnusedDeclaration"}) @MainThread protected void onPostExecute(Result result) { } /** * 在publishProgress之后调用,用来更新计算进度 * 所在线程:UI线程 */ @MainThread protected void onProgressUpdate(Progress... values) { } @MainThread protected void onCancelled(Result result) { onCancelled(); } /** * cancel被调用并且doInBackground执行结束,会调用onCancelled,表示任务被取消 * 这个时候onPostExecute不会再被调用,二者是互斥的,分别表示任务取消和任务执行完成 * 所在线程:UI线程 */ @MainThread protected void onCancelled() { } public final boolean isCancelled() { return mCancelled.get(); } public final boolean cancel(boolean mayInterruptIfRunning) { mCancelled.set(true); return mFuture.cancel(mayInterruptIfRunning); } public final Result get() throws InterruptedException, ExecutionException { return mFuture.get(); } public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return mFuture.get(timeout, unit); } /** * 这个方法如何执行和系统版本有关,在AsyncTask的使用规则里已经说明,如果你真的想使用并行AsyncTask, * 也是可以的,只要稍作修改 * 必须在UI线程调用此方法,5.2之后这不是必须的 */ @MainThread public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } /** * 通过这个方法我们可以自定义AsyncTask的执行方式,串行or并行,甚至可以采用自己的Executor * 为了实现并行,我们可以在外部这么用AsyncTask: * asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Params... params); * 必须在UI线程调用此方法,5.2之后这不是必须的 */ @MainThread public final AsyncTask executeOnExecutor(Executor exec, Params... params) { if (mStatus != Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); } } mStatus = Status.RUNNING; //任务执行之前这里调用了onPreExecute onPreExecute(); mWorker.mParams = params; //然后后台计算doInBackground才准备开始 exec.execute(mFuture); return this;}/**这是AsyncTask提供的一个静态方法,方便我们直接执行一个runnable*/@MainThreadpublic static void execute(Runnable runnable) { sDefaultExecutor.execute(runnable);}/**打印后台计算进度,onProgressUpdate会被调用*/@WorkerThreadprotected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult
AsynTask是如何实现串行的?
public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);} public final AsyncTask executeOnExecutor(Executor exec, Params... params) { if (mStatus != Status.PENDING) { switch (mStatus) { case RUNNING: throw new IllegalStateException("Cannot execute task:" + " the task is already running."); case FINISHED: throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)"); } } mStatus = Status.RUNNING; //任务执行之前这里调用了onPreExecute onPreExecute(); mWorker.mParams = params; //然后后台计算doInBackground才准备开始 exec.execute(mFuture); return this;}
是不是上面看不出什么来?那是必须的,AsyncTask实现串行是通过控制Executor .execute方法来实现的。
private static class SerialExecutor implements Executor { //线性双向队列,用来存储所有的AsyncTask任务 final ArrayDeque mTasks = new ArrayDeque(); //当前正在执行的AsyncTask任务 Runnable mActive; public synchronized void execute(final Runnable r) { //将新的AsyncTask任务加入到双向队列中 mTasks.offer(new Runnable() { public void run() { try { r.run();//执行AsyncTask任务 } finally { /** * 当前AsyncTask任务执行完毕后,进行下一轮执行,如果还有未执行任务的话 * 这一点很明显体现了AsyncTask是串行执行任务的,总是一个任务执行完毕才会执行下一个任务 */ scheduleNext(); } } }); //如果当前没有任务在执行,直接进入执行逻辑 if (mActive == null) { scheduleNext(); } } //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行 protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } }}
1、 当任务来临时,首先就是加入队列,然后判断当前有没有任务正在执行;
2、 如果没有任务正在执行则直接调用scheduleNext(),执行任务。
3、 当当前任务执行完成又再次在finally块调用scheduleNext执行下一个任务。这样就实现了串行执行任务,当然串行执行任务也是使用了线程池,那还用说是吧。
那如果我们想要并行,如何操作,很简单之间替换默认的SerialExecutor ,就是因为它,才串行的,所以我们只要像这样就可以实现并行执行任务了:
new MyAsyncTask("MyAsyncTask1").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");
其实AsyncTask源码并不多,文章到此结束,欢迎拍砖。
更多相关文章
- Android编程: 调试方法
- ListView常用属性、方法
- Android多线程(一)
- Android 控件(button)对齐方法实现详解
- Android Studio更新升级方法
- android 实现 APP 保活且正常升级的方法