写点什么

深入理解 AsyncTask 的工作原理,成为阿里 P7Android 架构师到底有多难

用户头像
Android架构
关注
发布于: 58 分钟前

4 mTaskInvoked.set(true);


5


6 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);


7 //noinspection unchecked


8 Result result = doInBackground(mParams);


9 Binder.flushPendingCommands();


10 return postResult(result);


11 }


12 };


13


14 mFuture = new FutureTask<Result>(mWorker) {


15 @Override


16 protected void done() {


17 try {


18 postResultIfNotInvoked(get());


19 } catch (InterruptedException e) {


20 android.util.Log.w(LOG_TAG, e);


21 } catch (ExecutionException e) {


22 throw new RuntimeException("An error occurred while executing doInBackground()",


23 e.getCause());


24 } catch (CancellationException e) {


25 postResultIfNotInvoked(null);


26 }


27 }


28 };


29 }


在第 2 行到第 12 行,初始化了 mWorker,它是一个派生自 WorkRunnable 类的对象。WorkRunnable 是一个抽象类,它实现了 Callable 接口。我们再来看一下第 4 行开始的 call 方法的定义,首先将 mTaskInvoked 设为 true 表示当前任务已被调用过,然后在第 6 行设置线程的优先级。在第 8 行我们可以看到,调用了 AsyncTask 对象的 doInBackground 方法开始执行我们所定义的后台任务,并获取返回结果存入 result 中。最后将任务返回结果传递给 postResult 方法。关于 postResult 方法我们会在下文进行分析。由此我们可以知道,实际上 AsyncTask 的成员 mWorker 包含了 AyncTask 最终要执行的任务(即 mWorker 的 call 方法)。


接下来让我们看看对 mFuture 的初始化。我们可以看到 mFuture 是一个 FutureTask 的直接子类(匿名内部类)的对象,在 FutureTask 的构造方法中我们传入了 mWorker 作为参数。我们使用的是 FutureTask 的这个构造方法:


public FutureTask(Callable<V> callable) {


if (callable == null)


throw new NullPointerException();


this.callable = callable;


this.state = NEW; // ensure visibility of callable


}


也就是说,mFuture 是一个封装了我们的后台任务的 FutureTask 对象,FutureTask 类实现了 FutureRunnable 接口,通过这个接口可以方便的取消后台任务以及获取后台任务的执行结果,具体介绍请看这里:Java 并发编程:Callable、Future 和 FutureTask。


从上面的分析我们知道了,当 mWorker 中定义的 call 方法被执行时,doInBackground 就会开始执行,我们定义的后台任务也就真正开始了。那么这个 call 方法什么时候会被调用呢?我们可以看到经过层层封装,实际上是 mFuture 对象封装了 call 方法,当 mFuture 对象被提交到 AsyncTask 包含的线程池执行时,call 方法就会被调用,我们定义的后台任务也就开始执行了。下面我们来看一下 mFuture 是什么时候被提交到线程池执行的。


首先来看一下 execute 方法的源码:


public final AsyncTask<Params, Progress, Result> execute(Params... params) {


return executeOnExecutor(sDefaultExecutor, params);


}


我们可以看到它接收的参数是 Params 类型的参数,这个参数会一路传递到 doInBackground 方法中。execute 方法仅仅是调用了 executeOnExecutor 方法,并将 executeOnExecutor 方法的返回值作为自己的返回值。我们注意到,传入了 sDefaultExecutor 作为 executeOnExecutor 方法的参数,那么 sDefaultExecutor 是什么呢?简单地说,它是 AsyncTask 的默认执行器(线程池)。AsyncTask 可以以串行(一个接一个的执行)或并行(一并执行)两种方式来执行后台任务,在 Android3.0 及以后的版本中,默认的执行方式是串行。这个 sDefaultExecutor 就代表了默认的串行执行器(线程池)。也就是说我们平常在 AsyncTask 对象上调用 execute 方法,使用的是串行方式来执行后台任务。


我们再来看一下 executeOnExecutor 方法都做了些什么:


1 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,


2 Params... params) {


3 if (mStatus != Status.PENDING) {


4 switch (mStatus) {


5 case RUNNING:


6 throw new IllegalStateException("Cannot execute task:"


7 + " the task is already running.");


8 case FINISHED:


9 throw new IllegalStateException("Cannot execute task:"


10 + " the task has already been executed "


11 + "(a task can be executed only once)");


12 }


13 }


14


15 mStatus = Status.RUNNING;


16


17 onPreExecute();


18


19 mWorker.mParam


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


s = params;


20 exec.execute(mFuture);


21


22 return this;


23 }


从以上代码的第 4 行到第 12 行我们可以知道,当 AsyncTask 对象的当前状态为 RUNNING 或 FINISHED 时,调用 execute 方法会抛出异常,这意味着不能对正在执行任务的 AsyncTask 对象或是已经执行完任务的 AsyncTask 对象调用 execute 方法,这也就解释了我们上面提到的局限中的最后一条。


接着我们看到第 17 行存在一个对 onPreExecute 方法的调用,这表示了在执行后台任务前确实会调用 onPreExecute 方法。


在第 19 行,把我们传入的 execute 方法的 params 参数赋值给了 mWorker 的 mParams 成员变量;而后在第 20 行调用了 exec 的 execute 方法,并传入了 mFuture 作为参数。exec 就是我们传进来的 sDefaultExecutor。那么接下来我们看看 sDefaultExecutor 究竟是什么。在 AsyncTask 类的源码中,我们可以看到这句:


private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;


sDefaultExecutor 被赋值为 SERIAL_EXECUTOR,那么我们来看一下 SERIAL_EXECUTOR:


public static final Executor SERIAL_EXECUTOR = new SerialExecutor();


现在,我们知道了实际上 sDefaultExecutor 是一个 SerialExecutor 对象,我们来看一下 SerialExecutor 类的源码:


1 private static class SerialExecutor implements Executor {


2 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();


3 Runnable mActive;


4


5 public synchronized void execute(final Runnable r) {


6 mTasks.offer(new Runnable() {


7 public void run() {


8 try {


9 r.run();


10 } finally {


11 scheduleNext();


12 }


13 }


14 });


15 if (mActive == null) {


16 scheduleNext();


17 }


18 }


19


20 protected synchronized void scheduleNext() {


21 if ((mActive = mTasks.poll()) != null) {


22 THREAD_POOL_EXECUTOR.execute(mActive);


23 }


24 }


25 }


我们来看一下 execute 方法的实现。mTasks 代表了 SerialExecutor 这个串行线程池的任务缓存队列,在第 6 行,我们用 offer 方法向任务缓存队列中添加一个任务,任务的内容如第 7 行到第 13 行的 run 方法定义所示。我们可以看到,run 方法中:第 9 行调用了 mFuture(第 5 行的参数 r 就是我们传入的 mFuture)的 run 方法,而 mFuture 的 run 方法内部会调用 mWorker 的 call 方法,然后就会调用 doInBackground 方法,我们的后台任务也就开始执行了。那么我们提交到任务缓存队列中的任务什么时候会被执行呢?我们接着往下看。


首先我们看到第三行定义了一个 Runnable 变量 mActive,它代表了当前正在执行的 AsyncTask 对象。第 15 行判断 mActive 是否为 null,若为 null,就调用 scheduleNext 方法。如第 20 行到 24 行所示,在 scheduleNext 方法中,若缓存队列非空,则调用 THREAD_POOL_EXECUTOR.execute 方法执行从缓存队列中取出的任务,这时我们的后台任务便开始你真正执行了。


通过以上的分析,我们可以知道 SerialExecutor 所完成的工作主要是把任务加到任务缓存队列中,而真正执行任务的是 THREAD_POOL_EXECUTOR。我们来看下 THREAD_POOL_EXECUTOR 是什么:


public static final Executor THREAD_POOL_EXECUTOR


= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,


TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);


从上面的代码我们可以知道,它是一个线程池对象。根据 AsyncTask 的源码,我们可以获取它的各项参数如下:


1 private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();


2 private static final int CORE_POOL_SIZE = CPU_COUNT + 1;


3 private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;


4 private static final int KEEP_ALIVE = 1;


5


6 private static final ThreadFactory sThreadFactory = new ThreadFactory() {


7 private final AtomicInteger mCount = new AtomicInteger(1);


8


9 public Thread newThread(Runnable r) {


10 return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());


11 }


12 };


13


14 private static final BlockingQueue<Runnable> sPoolWorkQueue =


15 new LinkedBlockingQueue<Runnable>(128);


由以上代码我们可以知道:


  • corePoolSize 为 CPU 数加一;

  • maximumPoolSize 为 CPU 数的二倍加一;

  • 存活时间为 1 秒;

  • 任务缓存队列为 LinkedBlockingQueue


现在,我们已经了解到了从我们调用 AsyncTask 对象的 execute 方法开始知道后台任务执行完都发生了什么。现在让我们回过头来看一看之前提到的 postResult 方法的源码:


private Result postResult(Result result) {


@SuppressWarnings("unchecked")


Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,


new AsyncTaskResult<Result>(this, result));


message.sendToTarget();


return result;


}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
深入理解AsyncTask的工作原理,成为阿里P7Android架构师到底有多难