Android AsyncTask 详解

请支持原创~~~

版本基于:Android R

0. 前言

将复杂工作异步化、线程化对于Android 的性能的提高起到了很重要的作用,Android 框架为开发提供了很多辅助程序,这一篇着重分析Android AsyncTask的原理。

1. AsyncTask 声明

public abstract class AsyncTask { ... }

AsyncTask 是一个抽象类,一个模板类,需要三个模板参数,类型分别是Params、Progress、Result;

在使用的时候需要继承AsyncTask 并指定参数类型;

2. AsyncTask 几个静态变量

2.1 sThreadFactory

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

AsyncTask 原理的关键因素之一,为 AsyncTask 的线程池创建线程。在 newThread() 函数被调用的时候就会创建新的 Thread。详细看 ThreadPoolExecutor 类中处理。

 

2.2 THREAD_POOL_EXECUTOR

    public static final Executor THREAD_POOL_EXECUTOR;static {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,new SynchronousQueue(), sThreadFactory);threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);THREAD_POOL_EXECUTOR = threadPoolExecutor;}

全局变量 THREAD_POOL_EXECUTOR是 AsyncTask 异步操作的线程池管理者和执行者。

这里先注意下 ThreadPoolExecutor 的构造,会将sThreadFactory 存放在成员变量threadFactory。

注意:

  • ThreadPoolExecutor 构造中参数 corePoolSize、maximumPoolSize、keepAliveTime不能小于0,maximumPoolSize 不能小于 corePoolSize,否则会抛出 IllegalArgumentException;
  • ThreadPoolExecutor 类的第一个参数 corePoolSize,指线程池中通用的线程个数,即使都是空闲的线程。当然可以通过 allowCoreThreadTimeOut() 配置通用线程的 timeout;
  • 这里第一个参数为 CORE_POOL_SIZE,值为1,即进程中 AsyncTask 对象依赖的通用线程只有一个,其可以按照顺序排队执行 task;
  • ThreadPoolExecutor  类的第二个参数 maximumPoolSize,指定线程池可以容纳的线程最大个数。但是如果这个数量超过 corePoolSize,等待 work 的 idle 线程会有个 timeout,也就是第三个参数 keepAlivetime;

详细在下面execute的时候解释。

2.3 SERIAL_EXECUTOR

    private static class SerialExecutor implements Executor {final ArrayDeque mTasks = new ArrayDeque();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);}}}

待执行的任务在这里排队,任务最终是会运行在线程池中的某个线程中。

  • step 1:最开始 mActive 为null,mTasks.offer() 将任务添加后并不会执行 run(),而是下面case进入scheduleNext();
  • step2:当线程池 execute()时,会触发mActive 的 run(),在执行完成会 finally 会继续下一个任务的执行,如果 mTasks 内没有任务则 mActive 为null,下次 execute() 的时候会再次循环step 1的操作;

这里的 execute() 和线程池的 execute() 后面找到源头的时候集中解释。

3. AsyncTask 的构造

虽然有三个构造函数,但是只有一个是外部可见的,其他都是hide

    public AsyncTask() {this((Looper) null);}/*** @hide*/public AsyncTask(@Nullable Handler handler) {this(handler != null ? handler.getLooper() : null);}/**** @hide*/public AsyncTask(@Nullable Looper callbackLooper) {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 uncheckedresult = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);throw tr;} finally {postResult(result);}return result;}};mFuture = new FutureTask(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);}}};}

3.1 AsyncTask构造必须在主线程

目前 AsyncTask 提供了三个构造函数,但是其他两个是 hide。

即对于应用端来说,AsyncTask的构造是无参的,即最终第三个构造被调用时,参数callbackLooper 为 null。最终,成员变量mHandler 类型为 InternalHandler,参数为 Looper.getMainLooper(),这就要求 AsyncTask 的构造必须在主线程中:

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

3.2 mWorker 就是要在线程池中执行的任务

    private static abstract class WorkerRunnable implements Callable {Params[] mParams;}

WorkerRunnable 其实是一个Callable,Callable 的核心函数就是call,mWorker 在实例时重写了这个call函数,而call函数调用是在FutureTask 中。

3.3 mFuture 管理任务状态和过程

mFuture 为FutureTask 类:

public class FutureTask implements RunnableFuture {

FutureTask 为RunnableFuture:

public interface RunnableFuture extends Runnable, Future {

即,FutureTask类为Runnable、Future,需要实现Runnable 的run(),也是实现Future的cancel、get V,Runnable 和Future 的源码,这里就不列出来了。

来看下FutureTask 的构造:

    public FutureTask(Callable callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW;       // ensure visibility of callable}

将mWorker 存起来,以便后面通过这个 Callable 对象调用 mWorker的 call() 函数,调用过程在下面的 execute() 详细解释。

注意一些概念:

每一个 AsyncTask 对象,都会有一个 mWorker 和 mFuture,但是进程内所有的 AsyncTask 都是要在全局的线程池中运行。

4. AsyncTask.execute()

这里就是 2.3 节说的源头,也是到了核心部分了,execute() 函数在 AsyncTask 中出现了多次,需要注意区别:

  • AsyncTask.execute(Params)
  • SerialExecutor.execute(final Runnable)
  • THREAD_POOL_EXECUTOR.execute(Runnable)

来分析下 AsyncTask.execute():

    public final AsyncTask execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}@MainThreadpublic 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();mWorker.mParams = params;exec.execute(mFuture);return this;}

4.1  task只能执行一次

默认的 AsyncTask status 为 PENDING,一旦启动本 task 后,status 状态就会发生改变,后面再次 execute() 的时候就会出现IllegalStateException。

即,对于AsyncTask 只会执行一次,execute() 函数也只会调用一次。

 

4.2 在进入线程池之前会调用onPreExecute

在进入线程池执行task 前,会在主线程中执行 onPreExecute(),尽可能的快点完成,不要阻塞主线程;

4.3 将params 带入task 

在调用完 onPreExecute() 之后,会将 params 传给 mWorks.mParams。

注意,mWorks 在 AsyncTask 构造的时候创建,创建的同时指定了 call() 函数。

 

4.4 调用 SERIAL_EXECUTOR.execute()

AsyncTask.execute() 中会调用 executeOnExecutor(),传入的参数为 sDefaultExecutor,即 SERIAL_EXECUTOR。

这里回到了2.3 节 SERIAL_EXECUTOR 的实例,mTasks 会新建一个Runnable,对应mFuture。

最开始mActive 为null,所以逻辑会先执行 scheduleNext(),将 mTasks 新建的Runnable 取出并赋值给mActive,下次的 scheduleNext 会在 finally() 触发。

mActive 会被传入THREAD_POOL_EXECUTOR.execute()。

最开始的时候肯定一堆问号的,不能着急,继续沿着 THREAD_POOL_EXECUTOR.execute() 继续分析下去:

libcore/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.javapublic void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}

这里不具体分析了,代码逻辑比较简单,这里受 SERIAL_EXECUTOR 管控,execute() 的时候会调用 addWorker(),而 addWorker() 中会新建一个Worker 实例,而 Worker 构造:

        Worker(Runnable firstTask) {setState(-1); // inhibit interrupts until runWorkerthis.firstTask = firstTask;this.thread = getThreadFactory().newThread(this);}

有两点:

  • 将 ThreadPoolExecutor.execute() 传下来的Runnable,即 SerialExecutor.mActive 存到了Worker对象中;
  • 回到了2.2 节,在 ThreadPoolExecutor 构造的时候会将 sThreadFactory 存放在成员变量threadFactory,最终会在这里调用newThread函数创建一个新的Thread。

继续回到 addWorker() 中,在构造 Worker 实例会会调用 Thread.start(),进而调用Runnable.run(),即调用 SerialExecutor.Active.run(),而 mActive 是 SerialExecutor 新建的Runnable,跟 mFuture 是对应关系。

OK,这样就基本形成了一张清晰的调用关系:

  • 黑色箭头线是正常的调用流程;
  • 蓝色箭头线则是简化后的流程,最终线程中运行的就是WorkerRunnable 中的call();

从图可以清晰的看到 doInBackground() 是在新的线程中执行,只不过这个线程是在全局的AsyncTask 中的线程池中。

请注意以下性能方面的要点

默认情况下,应用会将其创建的所有 AsyncTask 对象推送到单个线程中。因此,它们按顺序执行,而且与主线程一样,特别长的工作数据包可能会阻塞队列。鉴于这个原因,我们建议您仅使用 AsyncTask 处理持续时间短于 5ms 的工作项。

4.5 InternalHandler 中的消息处理

  • MESSAGE_POST_PROGRESS

这个消息是为了让UI thread 刷新数据时使用,可以在 doInBackground() 中调用 publishProgress()函数实现消息跨线程传输;

  • MESSAGE_POST_RESULT

在线程池中 doInBackground() 完成后会在finally 中调用 postResult 发送消息告知主线程result;

也可以在FutureTask中干预后(cancel 调用)执行done 函数发送result;

5. AsyncTask.cancel(boolean)

cancel 有个参数mayInterruptIfRunning,返回值为boolean。

  • task 已经completed 或者是已经cancled 或者是因为特殊原因不能cancel 时调用会返回失败;
  • 如果task 还没有start 时,此时调用cancel,那task 将不再运行;
  • 如果task 已经start,则根据参数mayInterruptRunning 决定是否尝试interrunpt thread;
  • 即使mayInterruptRunning 为true,也不会等待task 运行完成的,cancel 一般在主线程中调用,与mWorker.call() 是两个线程,参数为true 只会调用thread.interrupt,而不会等待thread 完成。

interrupt是中断线程。如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。

其实,Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(这个状态不在Thread的属性上),interrupt方法仅仅只是将该状态置为true。

FutureTask 中在cancel 或者是mWorker.call() 调用完成后,会调用finishCompletion(),最终会回调done,也就是AsyncTask 构造中定义的,详见第3节,最终调用到postResultIfNotInvoked:

    private void postResultIfNotInvoked(Result result) {final boolean wasTaskInvoked = mTaskInvoked.get();if (!wasTaskInvoked) {postResult(result);}}
  • 如果task 已经运行,则这个postResult 会由call() 调用;
  • 如果task 没有运行,说明是被cancel触发的;

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

这个就是4.5 节中提到的MESSAGE_POST_RESULT的由来,Handler 是主线程中运行的,详细看第3.1 节。

这里需要注意发送消息时带的参数是AsyncTaskResult(this, result),来看下处理:

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

这里result 就是在postResult 传入的,mTask 为AsyncTask,mData就是从FutureTask 传下来的result。

最终调用的是AsyncTask.finish():

    private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {onPostExecute(result);}mStatus = Status.FINISHED;}
  • 如果是调用了cancel 接口,会触发回调onCancelled;
  • 如果是正常运行,则会触发回调onPostExecute;

至此,AsyncTask 剖析基本上是完成了。总结下:

  • AsyncTask 是一个轻量级的消息处理类,因为进程中都依赖线程池中同一个线程,所以task尽可能的快速处理;
  • AsyncTask 中有几个全局的变量需要关注;
  • AsyncTask 通过FutureTask 管理状态,通过SerialExecutor 管理排队的task,通过THREAD_POOL_EXECUTOR执行task;
  • AsyncTask 存在线程间消息通信,例如从task thread 发送cancel 和post 消息。而这些消息处理,需要线程POOL 在AsyncTask过程中没有创建出来,那么这个天生存在的POOL 只会是主线程。所以,要求AsyncTask 的构造必须是在主线程中;
  • AsyncTask 的cancel 接口带的参数,只是用来确认是否进行thread.interrupt(),而并不是阻塞线程或等待线程处理结束;
  • AsyncTask 是一个短期的单任务,task 运行结束后如果想要再次运行,需要重新建一个AsyncTask实例,而不是单纯的用原来的实例调用execute即可的;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部