转载请标明出处:【顾林海的博客】

个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!

##前言

AsyncTask是一种轻量级的异步任务类,内部封装了Thread和Handler,通过AsyncTask执行后台任务以及在主线程中访问UI控件,但AsyncTask在Android 1.6之前是串行任务,在Android 1.6时AsyncTask采用线程池处理并行任务,又在Android 3.0开始采用一个线程串行执行任务,所以在使用AsyncTask时需要根据具体的场景来选择是否使用AsyncTask,比如需要与主线程有交互可以使用AsyncTask,否则就使用线程,如果需要执行大量线程执行任务时推荐使用线程池,AsyncTask的使用方式并不会讲解,这个大家可以在网上随便搜搜,文章主要围绕AsyncTask的源码来解析执行的流程。

##源码解析

在使用AsyncTask时,需要重写它的几个方法,其中doInBackground(Params… params)方法必须实现,该方法用于执行异步任务,参数params表示异步任务的输入参数,在这个方法中可以通过publishProgress方法来更新任务进度,publishProgress方法会调用onProgressUpdate方法,改方法在主线程中执行;如果需要在执行异步之前做一些初始化操作,比如显示一个进度条,可以重写它的onPreExecute方法,这个方法执行在主线程;当doInBackground方法执行完毕,如果需要异步任务数据返回给主线程,可以重写onPostExecute(Result result)方法,这个方法在主线程中执行,参数result是后台返回的值,也就是doInBackground方法返回的值,那么AsyncTask整体执行的流程可以用下面的图来表示。

执行AsyncTask的方法是execute,贴出其中一个execute方法源码:

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

在执行AsyncTask的execute(Params… params)方法时,内部会去执行executeOnExecutor方法,其中的参数sDefaultExecutor是AsyncTask内部类SerialExecutor的实例,是一个串行的线程池,SerialExecutor实现Executor接口并实现Executor接口中的execute(Runnable command)方法,用于执行已经提交的Runnable任务对象,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);}}
}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;
}

SerialExecutor类中定义了一个ArrayDeque,它是一个先进先出的队列,在execute方法中通过scheduleNext方法从队列中取出Runnable对象,并通过THREAD_POOL_EXECUTOR来执行,THREAD_POOL_EXECUTOR是线程池。上面的SerialExecutor是用于任务的排队,而线程池THREAD_POOL_EXECUTOR才是执行任务的,关于SerialExecutor的介绍暂时就到这里,我们会到上面的execute方法。

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

通过execute(Params… params)方法执行executeOnExecutor(Executor exec,Params… params)方法,在一开始会对当前的执行的状态进行判断,Status是一个枚举类,内部提供了三种状态,分别是FENDING(表示尚未执行)、RUNNING(表示任务正在执行)和FINISHED(表示任务执行结束),AsyncTask内部在执行任务前会去判断当前任务的执行状态,当任务正在执行或是已经执行结束会抛出异常,当新任务开始时mStatus为RUNNING,表示开始执行异步任务,接着会调用onPreExecute()方法,这时还没有执行异步任务,所以onPreExecute方法在主线程中执行。
onPreExecute方法执行完毕后,会将我们传入的Params参数封装成FutureTask对象,其中的mWorker是内部静态类WorkerRunnable,源码如下。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {Params[] mParams;
}

WorkerRunnable实现了Callable接口,用于获取异步任务执行完毕后的数据,上面的mWorker和mFuture在AsyncTask初始化时进行初始化,看下面的源码。

public AsyncTask() {this((Looper) null);
}public AsyncTask(@Nullable Looper callbackLooper) {mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()? getMainHandler()//--------------1: new Handler(callbackLooper);mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);//————2Result result = null;try {Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}};mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected 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);}}};
}

上面1处代码判断callbackLooper是否空或是主线程的Looper,这个因为传入的是null,最终调用的是getMainHandler方法,用于创建Handler,这部分后面会讲到,继续看mWorker的实例化时实现了call方法,并返回异步任务执行结果Result,这里又看到一个熟悉的方法doInBackground方法,这个方法执行在异步线程中,mFuture在实例化时将mWorker作为参数并实现了done方法,可以看到内部通过get()方法获取mWorker的call方法的返回值,并传递给postResultlfNotInvoked方法,这里看下它的源码。

private void postResultIfNotInvoked(Result result) {final boolean wasTaskInvoked = mTaskInvoked.get();if (!wasTaskInvoked) {postResult(result);}
}

mTaskInvoked是AtomicBoolean类型,在上面执行mWorker的call方法时(代码2处)mTaskInvoked设置为true,因此这里的判断语句不成立,就不继续往下看了,回到上面的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 uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}
};

在执行完doInBackground方法后会返回Result,最后会执行finally语句块中的postResult方法,查看该方法源码:

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

很明显通过Handler来发送消息,找找Handler的定义的地方,还记得我们在上面介绍AsyncTask初始化时讲到传入的参数callbackLooper为空,就调用getMainHandler方法,也就是说这里的发送消息的Handler就是在getMainHandler方法中初始化的,查看getMainHandler方法源码。

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

InternalHandler是AsyncTask的内部静态类并继承自Handler。

private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}
}

之前在doInBackground方法执行完毕后会将Result和MESSAGE_POST_RESULT通过Handler发送给主线程,在handleMessage方法中将我们的Result包装成AsyncTaskResult类型,AsyncTaskResult内部有个Data范型数组,数组的第一个就是异步任务执行的结果,这里将结果传递给finish方法。

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

在这里又看到一个熟悉的方法onPostExecute方法,这个方法是执行在主线程中的用于与UI交互,执行完onPostExecute方法后会将mStatus设置为FINISHED,说明此次异步任务执行完毕。到这里onPreExecute、doInBackground和onPostExecute方法执行时机都已经讲解清楚了,剩下的就是onProgressUpdate方法的执行时机,从前面知道在doInBackground方法中执行publishProgress方法会执行onProgressUpdate方法,查看publishProgress方法源码。

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

在这里也是通过Handler来发送消息给主线程来执行onProgressUpdate方法。

private static class InternalHandler extends Handler {public InternalHandler(Looper looper) {super(looper);}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {case MESSAGE_POST_RESULT:// There is only one resultresult.mTask.finish(result.mData[0]);break;case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}
}

发送消息到主线程后会执行onProgressUpdate方法,这也说明了onProgressUpdate是执行在主线程中。

在这里提一下,之前在前言中讲过由于Android版本的不同,AsyncTask内部执行的机制也不同,这里AsyncTask新增了一个方法executeOnExecutor(Executor exec,Params… params)用于开发者自己定义线程池。

Android之AsyncTask源码解析相关推荐

  1. http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

    http://a.codekk.com/detail/Android/grumoon/Volley 源码解析

  2. BAT高级架构师合力熬夜15天,肝出了这份PDF版《Android百大框架源码解析》,还不快快码住。。。

    前言 为什么要阅读源码? 现在中高级Android岗位面试中,对于各种框架的源码都会刨根问底,从而来判断应试者的业务能力边际所在.但是很多开发者习惯直接搬运,对各种框架的源码都没有过深入研究,在面试时 ...

  3. Android通知系统源码解析

    Android通知系统源码解析 1. 概述 2. 流程图 2.1. 发送通知流程图 3. 源码解析 3.1. 使用通知--APP进程 3.1.1. 创建通知: 3.1.2. 发送(更新)通知: 3.1 ...

  4. Android AsyncTask源码解析

    在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行.在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只 ...

  5. AsyncTask源码解析,你需要摸清的细节

    AsyncTask简介 1. AsyncTask提供了一种恰当的简单的跟UI Thread交互的方式. 2. 它不需要通过操控Threads或Handler就可以将后台操作以及其结果展示在UI Thr ...

  6. Android Gradle Plugin 源码解析(上)

    一.源码依赖 本文基于: android gradle plugin版本: com.android.tools.build:gradle:2.3.0 gradle 版本:4.1 Gradle源码总共3 ...

  7. Android之EasyPermissions源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 我们知道在Android中想要申请权限就需要在AndroidManifest ...

  8. Android之DiskLruCache源码解析

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/73863258 本文出自:[顾林海的博客] 个人开发的微信小程序,目前功 ...

  9. Android Hawk的源码解析,一款基于SharedPreferences的存储框架

    转载请标注:http://blog.csdn.net/friendlychen/article/details/76218033 一.概念 SharedPreferences的使用大家应该非常熟悉啦. ...

最新文章

  1. 二年级上册计算题_小学二年级数学上册应用题与思维训练集锦500题
  2. SQL Server中的几个方法和Transact SQL 常用语句以及函数[个人推荐]
  3. c++学习笔记之模板
  4. 网上找工作秘籍(3)
  5. 程序如何在两个gpu卡上并行运行_深度学习分布式训练相关介绍 - Part 1 多GPU训练...
  6. [APIO2016]
  7. dynamo python修改多个参数_python之函数
  8. 10年老分析师最终抛弃Excel,它不是最好的数据分析工具
  9. 蒙文字体怎么安装_我们来聊一聊iOS13的“字体”该怎么用?
  10. CF620E New Year Tree
  11. linux和windows时间同步问题(UTClocaltime)
  12. 【深度学习】训练集、测试集和验证集
  13. 3行代码,Python实现excel转换成任意格式的word文档
  14. element table相同数据行合并
  15. 深入了解scratch中的“移动10步”和(你真的了解scratch吗?scratch初学者值得一看)
  16. perl中bless的理解
  17. 身份证正则 身份证正则表达式
  18. CX51 用户手册----MDU_F120伪指令
  19. java封装怎么写_java中封装怎么写
  20. 牛客网 - Ricky’s RealDan’s Ricky(博弈)

热门文章

  1. 压缩比13为什么建议用92的油_92号和95号汽油有什么区别,可以混着用吗?
  2. rz sz命令_5分钟学linux命令之split
  3. cheked复选框返回值的时候选中
  4. asp.net core学习笔记
  5. 孰轻孰重:可穿戴式设备的助益与风险
  6. 常见的和端口,IP相关的企业面试题
  7. Selenium支持高版本的FireFox
  8. 国内国外虚拟主机的对比
  9. CCNet 的 Build 流程
  10. Python的基本运算符