AsyncTask机制原理解析

Android为我们提供了2种方便的异步处理方案,Handler和AsyncTask,两种方式适合的场景网上一搜就知道了,但是为什么呢?这篇分析将为你揭晓答案。前面分析了Handler的机制原理,还不熟悉的可以看下Handler机制原理。通过本篇的学习,来了解AsyncTask的工作原理。本篇内容较多,本着结论先行,分析在后的原则来看。

一,使用

使用呢是十分的简单,用一个类继承AsyncTask,拿着这个对象,执行execute就可以开始执行后台任务了。还是找个例子吧

* private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
*     protected Long doInBackground(URL... urls) {
*         int count = urls.length;
*         long totalSize = 0;
*         for (int i = 0; i < count; i++) {
*             totalSize += Downloader.downloadFile(urls[i]);
*             publishProgress((int) ((i / (float) count) * 100));
*             // Escape early if cancel() is called
*             if (isCancelled()) break;
*         }
*         return totalSize;
*     }
*
*     protected void onProgressUpdate(Integer... progress) {
*         setProgressPercent(progress[0]);
*     }
*
*     protected void onPostExecute(Long result) {
*         showDialog("Downloaded " + result + " bytes");
*     }
* }
*new DownloadFilesTask().execute(url1, url2, url3);

这个例子是AsyncTask源码中注释提到的例子。我们看到AsyncTask有3个泛型参数,分别代表什么意思呢

 *     <li><code>Params</code>, the type of the parameters sent to the task upon*     execution.</li>*     <li><code>Progress</code>, the type of the progress units published during*     the background computation.</li>*     <li><code>Result</code>, the type of the result of the background*     computation.</li>

简单来说,第一个参数(是个可变参数)是传给后台任务用的,第二个参数是后台任务执行过程中更新进度使用,第三个参数是后台任务执行完的返回结果。除此之外,还有几个方法介绍一下

 * <p>AsyncTask must be subclassed to be used. The subclass will override at least* one method ({@link #doInBackground}), and most often will override a* second one ({@link #onPostExecute}.)</p>

至少重写一个方法,doInBackground,这个方法用来执行后台任务的,运行在工作线程

    @WorkerThreadprotected abstract Result doInBackground(Params... params);

通常,我们还会重写一个onPostExecute,这个方法是将后台任务的结果post给UI线程,运行在UI线程

    @MainThreadprotected void onPostExecute(Result result) {}

其实,另外还有2个方法我们需要关注下,这里将4个方法的注释都贴出来

 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>* <ol>*     <li>{@link #onPreExecute()}, invoked on the UI thread before the task*     is executed. This step is normally used to setup the task, for instance by*     showing a progress bar in the user interface.</li>*     <li>{@link #doInBackground}, invoked on the background thread*     immediately after {@link #onPreExecute()} finishes executing. This step is used*     to perform background computation that can take a long time. The parameters*     of the asynchronous task are passed to this step. The result of the computation must*     be returned by this step and will be passed back to the last step. This step*     can also use {@link #publishProgress} to publish one or more units*     of progress. These values are published on the UI thread, in the*     {@link #onProgressUpdate} step.</li>*     <li>{@link #onProgressUpdate}, invoked on the UI thread after a*     call to {@link #publishProgress}. The timing of the execution is*     undefined. This method is used to display any form of progress in the user*     interface while the background computation is still executing. For instance,*     it can be used to animate a progress bar or show logs in a text field.</li>*     <li>{@link #onPostExecute}, invoked on the UI thread after the background*     computation finishes. The result of the background computation is passed to*     this step as a parameter.</li>* </ol>

onPreExecute,在任务开始前的准备工作,运行在UI线程,比如准备进度条

onProgressUpdate,一般用在后台任务进行时,更新进度等动作,运行在UI线程

二,原理分析

分析之前,我们先说结论,AsyncTask是对线程池和Handler的封装,任务只能执行一次,多次执行报错,任务可取消,默认串行执行。

2.1,成员分析

    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;

这里能看到,通过获取CPU的核数,来确定核心池数量和最大池数量,这个池是啥,应该就是线程池吧。

    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());}};private static final BlockingQueue<Runnable> sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128);

这里有个ThreadFactory,其实就是生产线程的工厂嘛,BlockingQueue,看名字是个阻塞的队列,其实就是放我们要执行的任务的。

    /*** An {@link Executor} that can be used to execute tasks in parallel.*/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;}

先看Executor注释,可以用来执行并行任务。这里有个静态final的对象,看名字,没错,这就是线程池了。不过,这里不是说并行吗,别急,往下看

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);}}}
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

这里还有个串行的Executor,是啥呢,看到关键的一行,通过THREAD_POOL_EXECUTOR去execute,到这我们似乎应该明白了,pool是默认用来执行异步任务的,那么这个SerialExecutor其实可以理解为一个容器,对接收到的任务暂存,串行交给pool去处理。这也验证了AsyncTask默认是串行执行的,当然了,你可以自己定制为并行执行。

    private static final int MESSAGE_POST_RESULT = 0x1;private static final int MESSAGE_POST_PROGRESS = 0x2;

2种类型的消息,不需要解释了吧。

    /*** Indicates the current status of the task. Each status will be set only once* during the lifetime of a task.*/public enum Status {/*** Indicates that the task has not been executed yet.*/PENDING,/*** Indicates that the task is running.*/RUNNING,/*** Indicates that {@link AsyncTask#onPostExecute} has finished.*/FINISHED,}

用这个枚举来标识任务的状态,看注释,每个任务只能执行一次。

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

到这里,知道了吧,AsyncTask内部有一个UI线程的Handler。

AsyncTask是可以取消任务的,其实是使用了FutureTask和Callable。 这里介绍一下,Callable是个借口,与Runnable类似,不同的是Runnable没有返回值,Callable可以指定返回值。而关于FutureTask,看一下源码注释吧

 * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or* {@link Runnable} object.  Because {@code FutureTask} implements* {@code Runnable}, a {@code FutureTask} can be submitted to an* {@link Executor} for execution.

意思是说,FutureTask可以封装Callable和Runnable,并且可以让Executor执行起来。FutureTask有两个关键方法,done,表示任务执行完,cacel,表示任务取消。AsyncTask之所以能取消,其实就是通过FutureTask来取消的

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

mWorker是实现了Callable的对象。看到这里,应该对AsyncTask的特点有了初步的认识,没关系,下面还会继续加深。

2.2,工作原理分析

先看一下AsyncTask的构造方法

public AsyncTask() {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;}};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);}}};}

这里初始化了2个成员,mWorker和mFuture,内部的方法并不会执行。那么就看看execute方法吧

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

这里说明了,方法运行在UI线程。

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

一上来,先对status进行判断和初始化(这里是一个任务只能执行一次的实现),接下来,看到了onPreExecute,就是我们可能需要重写的方法。然后把params赋给了mWorker。exec.execute就是sDefaultExecutor执行execute。又因为

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

所以也就是SERIAL_EXECUTOR的execute了。

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

这里我们前面分析过,是用了个容器,让任务串行执行。传给pool的是mActive,其实是对mFuture的封装,而mFuture初始化又是使用的mWorker,所以最终执行到了mWorker的call方法,也就构造方法中的第一个成员的方法。这里会执行doInbackground,这里是在工作线程执行的。执行完会得到一个result,在finally中post出去。

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

Handler前面已经介绍过了,这里知道封装完Message,发送给UI线程即可。AsyncTaskResult是一个内部类

    private static class AsyncTaskResult<Data> {final AsyncTask mTask;final Data[] mData;AsyncTaskResult(AsyncTask task, Data... data) {mTask = task;mData = data;}}

看一下handleMessage

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

可以看到,POST_RESULT会调用finish方法

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

这里我们看到了熟悉的onPostExecute方法,参数是result。那么POST_PROGRESS是怎么处理的呢?我们知道,doInBackground可能需要较长的时间执行完,中间的过程想更新进度条怎么办呢?可以在doInbackground中调用publishProgress

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

如果任务没被取消,则通过Handler发送出去,那么接下来又到handleMessage了,看一下前面的代码,onProgressUpdate

    @MainThreadprotected void onProgressUpdate(Progress... values) {}

看到这里,工作原理分析就结束了,我们已经能知道AsyncTask是对线程池和Handler的封装,任务只能执行一次,任务可以取消,默认串行执行(注意理解为什么有这些特点)

AsyncTask机制原理解析相关推荐

  1. Handler机制原理解析(二)prepare,loop,post

    Handler机制原理解析(二)prepare,loop,post 上一篇已经介绍了Handler机制的原理,如果不熟悉可以看Handler机制原理解析(一).这一篇,介绍下Handler周边的知识点 ...

  2. Handler机制原理解析(一)

    Handler机制原理解析(一) 我们都知道,在Android中,主线程也叫UI线程是负责界面更新的,子线程或者工作线程适合做网络请求,数据库等耗时操作.如果在主线程中执行耗时操作可能引发ANR异常. ...

  3. python解析原理_Python语法垃圾回收机制原理解析

    一 引入 解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,那 ...

  4. android Handler机制原理解析(一篇就够,包你形象而深刻)

    首先,我将Handler相关的原理机制形象的描述为以下情景: Handler:快递员(属于某个快递公司的职员) Message:包裹(可以放置很多东西的箱子) MessageQueue:快递分拣中心( ...

  5. Binder通信机制原理解析

    Binder是什么?Binder有啥用?作为一个应用开发者,如果我们开发的应用不涉及跨进程通信(IPC),我想我们也不会去接触Binder.但不知你有没有发现,近来的Andorid面试,都会问及And ...

  6. RCU锁机制原理解析

    背景 为了保护共享数据,需要一些同步机制,如自旋锁(spinlock),读写锁(rwlock),它们使用起来非常简单,而且是一种很有效的同步机制,在UNIX系统和Linux系统中得到了广泛的使用.但是 ...

  7. Hadoop的HA机制原理解析,与HA高可用集群环境搭建

    2019独角兽企业重金招聘Python工程师标准>>> Hadoop的HA工作机制示意图 下面我们开始搭建这一套高可用集群环境 hadoop2.0已经发布了稳定版本了,增加了很多特性 ...

  8. Android 签名机制原理解析和V1 、V2签名区别

    一.什么是签名? 是确保消息来源的真实性 是确保消息不会被第三方篡改 1.基本信息基础必备 1.1 消息摘要 消息摘要,又称数字摘要 或 数字指纹.  简单来说,消息摘要就是在消息数据上,执行一个单向 ...

  9. python 参数收集_Python参数传递及收集机制原理解析

    python参数传递时,主要有位置参数和关键字参数. 1. 位置参数:顾名思义,参数的位置顺序很重要,因为是直接根据位置赋值的. def func1(a, b): print(a,b) # 位置参数, ...

最新文章

  1. linux系统版本间的区别是什么?内核又是什么
  2. IDC数据中心机房该如何节能
  3. 喜报 | 链家签约神策数据,让数据“说话”更精准
  4. linux环境禁用apache目录浏览功能
  5. Ribbon-4 Ribbon脱离Eureka使用
  6. python特性和属性的区别_什么是属性,什么是特性,有何不同?
  7. 安卓最新系统_安卓最新10.0系统,新增功能都在这了!
  8. [蓝桥杯][2017年第八届真题]正则问题(DFS)
  9. Python去除字符串中的特殊字符(包括空格)
  10. SpringBoot2.0之六 多环境配置
  11. 在Unity编辑器中开发遇到问题BUG如何调试解决?
  12. 连续整数--全国模拟(一)
  13. 项目管理术语中英文对照
  14. JavaScript闭包
  15. 【Vue】前端跨域解决方法
  16. 项目管理文档目录结构
  17. Vue2.0 Vue组件库
  18. c语言 文件指针移动一位,c语言怎样移动文件指针到制定位置?
  19. 解析小型机、大型机和PC服务器间的差别
  20. CircularProgressIndicator

热门文章

  1. 攻防世界-command_execution
  2. ARM 在Unity3D 中的美术优化解决方案 2, 几何体
  3. NOIP2011 复盘
  4. Idea配置JRebel插件的详细配置及图解
  5. C基础知识总结(全)
  6. 时尚品牌玛丝菲尔,选择华为云会议的3个理由
  7. 架构师的软实力之治理、技术诀窍
  8. asp是什么,什么是ASP?ASP的两种解释
  9. 相控阵基础之2-移相与时延
  10. codemirror 简单使用