AsyncTask是Android中常用的异步任务解决方案,在面试中面试官经常会问到有关AsyncTask相关的点,很多人只知道如何去用AsyncTask,没有深入过源码理解其原理,这在面试的时候往往对自己不利,本文从源码角度解读AsyncTask的原理及使用,相信读完本文你就可以很好地应对Android面试中有关AsyncTask的问题。

首先,AsyncTask是一个抽象类,我们使用的时候需要自定义一个类去继承AsyncTask,在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:

  1. Params
    在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  2. Progress
    后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  3. Result
    当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

AsyncTask这个抽象类中有一个抽象方法protected abstract Result doInBackground(Params... params),所以如果要自定义一个自己的AsyncTask必须实现该抽象方法,在doInBackground中做自己的耗时操作的逻辑。

几个回调方法:

  1. onPreExecute()
    这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

  2. doInBackground(Params…)
    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。

  3. onProgressUpdate(Progress…)
    当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

  4. onPostExecute(Result)
    当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。


放一张AsyncTask的核心逻辑图,大家可以先看图再看后面的具体介绍,读完介绍之后再回过头来看一下这张图,理解会更深刻(建议查看大图):

如果还看不清见原图地址:https://github.com/DmrfCoder/interview/blob/master/resource/AsyncTask.png

构造方法

 public AsyncTask(@Nullable Looper callbackLooper) {        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;            }        };        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);                }            }        };    }

在构造方法中没有执行过多的逻辑,只是初始化了三个对象:Handler mHandlerWorkerRunnable mWorkerFutureTask mFuture

接着要想启动一个任务,我们就需要调用该任务的excute()方法

excute()

excute()方法的源码:

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

excute()源码中调用了executeOnExecutor方法,传入了两个参数:sDefaultExecutorparams

excuteOnExcutor()

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();mWorker.mParams = params;exec.execute(mFuture);return this;}

在该方法中先是调用了onPreExecute()方法,因此证明了onPreExecute()方法会第一个被调用,然后执行了exec.execute(mFuture)方法,这个exec实际上从excute()方法中传入的sDefaultExecutor

sDefaultExecutor

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

这里先new出了一个SERIAL_EXECUTOR常量,然后将sDefaultExecutor的值赋值为这个常量,也就是说明,刚才在executeOnExecutor()方法中调用的execute()方法,其实也就是调用的SerialExecutor类中的execute()方法。

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);}}}

SerialExecutor类中有一个ArrayDeque的队列,还有一个当前的Runnable mActive对象,在execute()方法中,首先会用mTasks.offer给队列的尾部加入一个匿名的Runnable类,在该Runnable匿名类中执行了excute中传入的Runnable对象的run方法,然后调用scheduleNext,该方法使用mTask.poll()取出队列头部的任务,然后调用THREAD_POOL_EXECUTOR.execute(mActive),这里的THREAD_POOL_EXECUTOR实际上是一个线程池,当当前队头的run方法执行完成之后又会在try..finally中调用scheduleNext()取出下一个任务进入线程池进行执行,所以可以看到,在AsyncTask中实际上有两个线程池,一个是SerialExcutor,另一个是THREAD_POOL_EXCUTOR,他们两是都是静态字段,对于所有的AsyncTask都会公用他们两,前者模仿的是单一线程池,用于做任务调度,利用队列将所有的任务排队,然后每次把队头的任务交给THREAD_POOL_EXCUTOR去做实际的执行,

注意excute方法中的Runnable参数,那么目前这个参数的值是什么呢?当然就是mFuture对象了,也就是说这里的r.run()实际上调用的是FutureTask类的run()方法,而我们刚才在构造mFuture的时候传入了mWorker,而mWorker的构造代码如下:

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;            }        };

WorkerRunnable是一个抽象类,其实现了Callable接口,所以在这里实现了Callable接口需要的``call方法,call方法里调用了doInBackground()方法,调用完成之后在try…finally中调用了postResult()方法将结果返回,而postResult()`中:

 private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }

在这里使用getHandler()拿到刚才构造方法中的mHandler对象,然后发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。而这个mHandler实际上是一个InternalHandler对象:

private static class InternalHandler extends Handler {        public InternalHandler(Looper looper) {            super(looper);        }        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})        @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;            }        }    }

可以看到在该InternalHanderhandleMessage方法中接收到了刚才发送的消息,并根据msg.what的不同调用了不同的逻辑,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。那么finish()方法的源码如下所示:

private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {onPostExecute(result);}mStatus = Status.FINISHED;}

可以看到,如果当前任务被取消掉了,就会调用onCancelled()``方法,如果没有被取消,则调用onPostExecute()方法,这样当前任务的执行就全部结束了。

我们注意到,在刚才InternalHandlerhandleMessage()方法里,还有一种MESSAGE_POST_PROGRESS的消息类型,这种消息是用于当前进度的,调用的正是onProgressUpdate()方法,那么什么时候才会发出这样一条消息呢?相信你已经猜到了,查看publishProgress()方法的源码,如下所示:

protected final void publishProgress(Progress... values) {if (!isCancelled()) {sHandler.obtainMessage(MESSAGE_POST_PROGRESS,new AsyncTaskResult<Progress>(this, values)).sendToTarget();}}

正因如此,在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。其实也没有什么神秘的,因为说到底,AsyncTask也是使用的异步消息处理机制,只是做了非常好的封装而已。

关于THREAD_POOL_EXECUTOR

刚才提到的THREAD_POOL_EXECUTOR:

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 workprivate 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;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;    }

可以看到,AsyncTask实际上是对线程池ThreadPoolExcutor的封装,在实例化ThreadPoolExcotor的时候传入的核心线程数是在2-4个之间,最大线程数是cpu count*2 1个,我们在SerialExecutorscheduleNext方法中使用该线程池去真正执行任务。

AsyncTask默认是串行执行任务的,如果想要并行,从Android 3.0开始AsyncTask增加了executeOnExecutor方法,用该方法可以让AsyncTask并行处理任务。方法签名如下:

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,            Params... params)

exec是一个Executor对象,为了让AsyncTask并行处理任务,第一个参数传入一个线程池对象。第二个参数params表示的是要执行的任务的参数

一个疑问

刚才提到了,AsyncTaskSerialExecutor线程池中做的调度是串行的,也就是说同时只会有一个线程被执行,那这里的ThreadPoolExcutor THREAD_POOL_EXECUTOR为什么没有初始化成singleThreadPool?这点我个人也不是很理解,之前面试的时候有请教过面试官,面试官说是起到调度作用,我个人猜测可能和AsyncTask并行执行任务有关,如上所述,如果想要让AsyncTask并行执行任务需要调用executeOnExecutor并传入线程池,而这里我们可以直接传入AsyncTask帮我们实例化好的线程池对象
AsyncTask.THREAD_POOL_EXECUTOR,这样就不用我们自己创建线程池了,比如:

Executor exec = new ThreadPoolExecutor(15, 200, 10,   TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());new DownloadTask().executeOnExecutor(exec);

当然这只是我个人的理解,欢迎大家在评论区留言发表自己的看法。

总结

以上就是关于AsyncTask源码的入门分析,希望大家再看一遍这张图:

更多相关文章

  1. Android中通过Messenger与Service实现进程间双向通信
  2. android service
  3. Android高性能编程
  4. android Widget添加过程和android添加widget不更新的问题分析解
  5. Android学习日记----------Android(安卓)10调用摄像头闪退问题--
  6. Android(安卓)开发艺术探索笔记-Activity启动方式
  7. Android(安卓)MediaPlayer的核心原理
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. Android 交错 GridView
  2. android中流的理解。(字节流读写与字符流
  3. 图片缓存库之深度剖析
  4. Android环境变量设置
  5. Android(安卓)中的那些策略模式
  6. Android SDK 源代码关联Eclipse
  7. Android Drawable的那些事儿
  8. Android Activity、Fragment之间的数据传
  9. Android 四大组件之---Activity 详解
  10. Android adb 命令总结_持续更新