AsyncTask源码分析

2019-05-03  本文已影响0人  有没有口罩给我一个

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:23
MyAsyncTask2 === 2019-05-03 07:35:26
MyAsyncTask3 === 2019-05-03 07:35:29
MyAsyncTask4 === 2019-05-03 07:35:32
MyAsyncTask5 === 2019-05-03 07:35:35
MyAsyncTask6 === 2019-05-03 07:35:38

从5个AsyncTask共耗时15s且时间间隔为3s,很显然是串行执行的。

既然下来我们来分析一下源码:

public abstract class AsyncTask<Params, Progress, Result> {
    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<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(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<Params, Result> mWorker;
    private final FutureTask<Result> 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会被加入到任务队列,然后排队,一个个执行。
     * 总结:
     * <p>
     * 当任务来临时,首先就是入队列,然后判断当前有没有任务正在执行
     * 如果没有任务执行则直接调用scheduleNext(),执行任务。
     * 当当前任务执行完成又再次在finally块调用scheduleNext执行下一个任务
     */
    private static class SerialExecutor implements Executor {
        //线性双向队列,用来存储所有的AsyncTask任务
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //当前正在执行的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<Params, Result>() {
            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<Result>(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<Result>(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<Params, Progress, Result> 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<Params, Progress, Result> 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*/
@MainThread
public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
}
/**打印后台计算进度,onProgressUpdate会被调用*/
@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
//任务结束的时候会进行判断,如果任务没有被取消,则onPostExecute会被调用
private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}
//AsyncTask内部Handler,用来发送后台计算进度更新消息和计算完成消息
private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
        super(looper);
    }
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams;}
private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;
    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
      }
    }
}

AsynTask是如何实现串行的?

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}


 public final AsyncTask<Params, Progress, Result> 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<Runnable> mTasks = new ArrayDeque<Runnable>();
    //当前正在执行的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源码并不多,文章到此结束,欢迎拍砖。

上一篇下一篇

猜你喜欢

热点阅读