写点什么

Android AsyncTask 源码解析

发布于: 2021 年 11 月 06 日
  1. ????protected?Boolean?doInBackground(Void...?params)?{??

  2. ????????try?{??

  3. ????????????while?(true)?{??

  4. ????????????????int?downloadPercent?=?doDownload();??

  5. ????????????????publishProgress(downloadPercent);??

  6. ????????????????if?(downloadPercent?>=?100)?{??

  7. ????????????????????break;??

  8. ????????????????}??

  9. ????????????}??

  10. ????????}?catch?(Exception?e)?{??

  11. ????????????return?false;??

  12. ????????}??

  13. ????????return?true;??

  14. ????}??

  15. ????@Override??

  16. ????protected?void?onProgressUpdate(Integer...?values)?{??

  17. ????????progressDialog.setMessage("当前下载进度:"?+?values[0]?+?"%");??

  18. ????}??

  19. ????@Override??

  20. ????protected?void?onPostExecute(Boolean?result)?{??

  21. ????????progressDialog.dismiss();??

  22. ????????if?(result)?{??

  23. ????????????Toast.makeText(context,?"下载成功",?Toast.LENGTH_SHORT).show();??

  24. ????????}?else?{??

  25. ????????????Toast.makeText(context,?"下载失败",?Toast.LENGTH_SHORT).show();??

  26. ????????}??

  27. ????}??

  28. }??


这里我们模拟了一个下载任务,在 doInBackground()方法中去执行具体的下载逻辑,在 onProgressUpdate()方法中显示当前的下载进度,在 onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:


[java]? view plain copy


  1. new?DownloadTask().execute();??


以上就是 AsyncTask 的基本用法,怎么样,是不是感觉在子线程和 UI 线程之间进行切换变得灵活了很多?我们并不需求去考虑什么异步消息处理机制,也不需要专门使用一个 Handler 来发送和接收消息,只需要调用一下 publishProgress()方法就可以轻松地从子线程切换到 UI 线程了。


分析 AsyncTask 的源码




虽然 AsyncTask 这么简单好用,但你知道它是怎样实现的吗?那么接下来,我们就来分析一下 AsyncTask 的源码,对它的实现原理一探究竟。注意这里我选用的是 Android 4.0 的源码,如果你查看的是其它版本的源码,可能会有一些出入。


从之前 DownloadTask 的代码就可以看出,在启动某一个任务之前,要先 new 出它的实例,因此,我们就先来看一看 AsyncTask 构造函数中的源码,如下所示:


[java]? view plain copy


  1. public?AsyncTask()?{??

  2. ????mWorker?=?new?WorkerRunnable<Params,?Result>()?{??

  3. ????????public?Result?call()?throws?Exception?{??

  4. ????????????mTaskInvoked.set(true);??

  5. ????????????Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);??

  6. ????????????return?postResult(doInBackground(mParams));??

  7. ????????}??

  8. ????};??

  9. ????mFuture?=?new?FutureTask<Result>(mWorker)?{??

  10. ????????@Override??

  11. ????????protected?void?done()?{??

  12. ????????????try?{??

  13. ????????????????final?Result?result?=?get();??

  14. ????????????????postResultIfNotInvoked(result);??

  15. ????????????}?catch?(InterruptedException?e)?{??

  16. ????????????????android.util.Log.w(LOG_TAG,?e);??

  17. ????????????}?catch?(ExecutionException?e)?{??

  18. ????????????????throw?new?RuntimeException("An?error?occured?while?executing?doInBackground()",??

  19. ????????????????????????e.getCause());??

  20. ????????????}?catch?(CancellationException?e)?{??

  21. ????????????????postResultIfNotInvoked(null);??

  22. ????????????}?catch?(Throwable?t)?{??

  23. ????????????????throw?new?RuntimeException("An?error?occured?while?executing?"??

  24. ????????????????????????+?"doInBackground()",?t);??

  25. ????????????}??

  26. ????????}??

  27. ????};??

  28. }??


这段代码虽然看起来有点长,但实际上并没有任何具体的逻辑会得到执行,只是初始化了两个变量,mWorker 和 mFuture,并在初始化 mFuture 的时候将 mWorker 作为参数传入。mWorker 是一个 Callable 对象,mFuture 是一个 FutureTask 对象,这两个变量会暂时保存在内存中,稍后才会用到它们。


接着如果想要启动某一个任务,就需要调用该任务的 execute()方法,因此现在我们来看一看 execute()方法的源码,如下所示:


[java]? view plain copy


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

  2. ????return?executeOnExecutor(sDefaultExecutor,?params);??

  3. }??


简单的有点过分了,只有一行代码,仅是调用了 executeOnExecutor()方法,那么具体的逻辑就应该写在这个方法里了,快跟进去瞧一瞧:


[java]? view plain copy


  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. ????mStatus?=?Status.RUNNING;??

  15. ????onPreExecute();??

  16. ????mWorker.mParams?=?params;??

  17. ????exec.execute(mFuture);??

  18. ????return?this;??

  19. }??


果然,这里的代码看上去才正常点。可以看到,在第 15 行调用了 onPreExecute()方法,因此证明了 onPreExecute()方法会第一个得到执行。可是接下来的代码就看不明白了,怎么没见到哪里有调用 doInBackground()方法呢?别着急,慢慢找总会找到的,我们看到,在第 17 行调用了 Executor 的 execute()方法,并将前面初始化的 mFuture 对象传了进去,那么这个 Executor 对象又是什么呢?查看上面的 execute()方法,原来是传入了一个 sDefaultExecutor 变量,接着找一下这个 sDefaultExecutor 变量是在哪里定义的,源码如下所示:


[java]? view plain copy


  1. public?static?final?Executor?SERIAL_EXECUTOR?=?new?SerialExecutor();??

  2. ……??

  3. private?static?volatile?Executor?sDefaultExecutor?=?SERIAL_EXECUTOR;??


可以看到,这里先 new 出了一个 SERIAL_EXECUTOR 常量,然后将 sDefaultExecutor 的值赋值为这个常量,也就是说明,刚才在 executeOnExecutor()方法中调用的 execute()方法,其实也就是调用的 SerialExecutor 类中的 execute()方法。那么我们自然要去看看 SerialExecutor 的源码了,如下所示:


[java]? view plain copy


  1. private?static?class?SerialExecutor?implements?Executor?{??

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

  3. ????Runnable?mActive;??

  4. ????public?synchronized?void?execute(final?Runnable?r)?{??

  5. ????????mTasks.offer(new?Runnable()?{??

  6. ????????????public?void?run()?{??

  7. ????????????????try?{??

  8. ????????????????????r.run();??

  9. ????????????????}?finally?{??

  10. ????????????????????scheduleNext();??

  11. ????????????????}??

  12. ????????????}??

  13. ????????});??

  14. ????????if?(mActive?==?null)?{??

  15. ????????????scheduleNext();??

  16. ????????}??

  17. ????}??

  18. ????protected?synchronized?void?scheduleNext()?{??

  19. ????????if?((mActive?=?mTasks.poll())?!=?null)?{??

  20. ????????????THREAD_POOL_EXECUTOR.execute(mActive);??

  21. ????????}??

  22. ????}??

  23. }??


SerialExecutor 类中也有一个 execute()方法,这个方法里的所有逻辑就是在子线程中执行的了,注意这个方法有一个 Runnable 参数,那么目前这个参数的值是什么呢?当然就是 mFuture 对象了,也就是说在第 9 行我们要调用的是 FutureTask 类的 run()方法,而在这个方法里又会去调用 Sync 内部类的 innerRun()方法,因此我们直接来看 innerRun()方法的源码:


[java]? view plain copy


  1. void?innerRun()?{??

  2. ????if?(!compareAndSetState(READY,?RUNNING))??

  3. ????????return;??

  4. ????runner?=?Thread.currentThread();??

  5. ????if?(getState()?==?RUNNING)?{?//?recheck?after?setting?thread??

  6. ????????V?result;??

  7. ????????try?{??

  8. ????????????result?=?callable.call();??

  9. ????????}?catch?(Throwable?ex)?{??

  10. ????????????setException(ex);??

  11. ????????????return;??

  12. ????????}??

  13. ????????set(result);??

  14. ????}?else?{??

  15. ????????releaseShared(0);?//?cancel??

  16. ????}??

  17. }??


可以看到,在第 8 行调用了 callable 的 call()方法,那么这个 callable 对象是什么呢?其实就是在初始化 mFuture 对象时传入的 mWorker 对象了,此时调用的 call()方法,也就是一开始在 AsyncTask 的构造函数中指定的,我们把它单独拿出来看一下,代码如下所示:


[java]? view plain copy


  1. public?Result?call()?throws?Exception?{??

  2. ????mTaskInvoked.set(true);??

  3. ????Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);??

  4. ????return?postResult(doInBackground(mParams));??

  5. }??


在 postResult()方法的参数里面,我们终于找到了 doInBackground()方法的调用处,虽然经过了很多周转,但目前的代码仍然是运行在子线程当中的,所以这也就是为什么我们可以在 doInBackground()方法中去处理耗时的逻辑。接着将 doInBackground()方法返回的结果传递给了 postResult()方法,这个方法的源码如下所示:


[java]? view plain copy


  1. private?Result?postResult(Result?result)?{??

  2. ????Message?message?=?sHandler.obtainMessage(MESSAGE_POST_RESULT,??

  3. ????????????new?AsyncTaskResult<Result>(this,?result));??

  4. ????message.sendToTarget();??

  5. ????return?result;??

  6. }??


如果你已经熟悉了异步消息处理机制,这段代码对你来说一定非常简单吧。这里使用 sHandler 对象发出了一条消息,消息中携带了 MESSAGE_POST_RESULT 常量和一个表示任务执行结果的 AsyncTaskResult 对象。这个 sHandler 对象是 InternalHandler 类的一个实例,那么稍后这条消息肯定会在 InternalHandler 的 handleMessage()方法中被处理。InternalHandler 的源码如下所示:


[java]? view plain copy


  1. private?static?class?InternalHandler?extends?Handler?{??

  2. ????@SuppressWarnings({"unchecked",?"RawUseOfParameterizedType"})??

  3. ????@Override??

  4. ????public?void?handleMessage(Message?msg)?{??

  5. ????????AsyncTaskResult?result?=?(AsyncTaskResult)?msg.obj;??

  6. ????????switch?(msg.what)?{??

  7. ????????????case?MESSAGE_POST_RESULT:??

  8. ????????????????//?There?is?only?one?result??

  9. ????????????????result.mTask.finish(result.mData[0]);??

  10. ????????????????break;??

  11. ????????????case?MESSAGE_POST_PROGRESS:??

  12. ????????????????result.mTask.onProgressUpdate(result.mData);??

  13. ????????????????break;??

  14. ????????}??

  15. ????}??

  16. }??


这里对消息的类型进行了判断,如果这是一条 MESSAGE_POST_RESULT 消息,就会去执行 finish()方法,如果这是一条 MESSAGE_POST_PROGRESS 消息,就会去执行 onProgressUpdate()方法。那么 finish()方法的源码如下所示:


[java]? view plain copy


  1. private?void?finish(Result?result)?{??

  2. ????if?(isCancelled())?{??

  3. ????????onCancelled(result);??

  4. ????}?else?{??

  5. ????????onPostExecute(result);??

  6. ????}??

  7. ????mStatus?=?Status.FINISHED;??

  8. }??


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


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


[java]? view plain copy


  1. protected?final?void?publishProgress(Progress...?values)?{??

  2. ????if?(!isCancelled())?{??

  3. ????????sHandler.obtainMessage(MESSAGE_POST_PROGRESS,??

  4. ????????????????new?AsyncTaskResult<Progress>(this,?values)).sendToTarget();??

  5. ????}??

  6. }??


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


读到这里,相信你对 AsyncTask 中的每个回调方法的作用、原理、以及何时会被调用都已经搞明白了吧。


关于 AsyncTask 你所不知道的秘密




不得不说,刚才我们在分析 SerialExecutor 的时候,其实并没有分析的很仔细,仅仅只是关注了它会调用 mFuture 中的 run()方法,但是至于什么时候会调用我们并没有进一步地研究。其实 SerialExecutor 也是 AsyncTask 在 3.0 版本以后做了最主要的修改的地方,它在 AsyncTask 中是以常量的形式被使用的,因此在整个应用程序中的所有 AsyncTask 实例都会共用同一个 SerialExecutor。下面我们就来对这个类进行更加详细的分析,为了方便阅读,我把它的代码再贴出来一遍:


[java]? view plain copy


  1. private?static?class?SerialExecutor?implements?Executor?{??

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

  3. ????Runnable?mActive;??

  4. ????public?synchronized?void?execute(final?Runnable?r)?{??

  5. ????????mTasks.offer(new?Runnable()?{??

  6. ????????????public?void?run()?{??

  7. ????????????????try?{??

  8. ????????????????????r.run();??

  9. ????????????????}?finally?{??

  10. ????????????????????scheduleNext();??

  11. ????????????????}??

  12. ????????????}??

  13. ????????});??

  14. ????????if?(mActive?==?null)?{??

  15. ????????????scheduleNext();??

  16. ????????}??

  17. ????}??

  18. ????protected?synchronized?void?scheduleNext()?{??

  19. ????????if?((mActive?=?mTasks.poll())?!=?null)?{??

  20. ????????????THREAD_POOL_EXECUTOR.execute(mActive);??

  21. ????????}??

  22. ????}??

  23. }??


可以看到,SerialExecutor 是使用 ArrayDeque 这个队列来管理 Runnable 对象的,如果我们一次性启动了很多个任务,首先在第一次运行 execute()方法的时候,会调用 ArrayDeque 的 offer()方法将传入的 Runnable 对象添加到队列的尾部,然后判断 mActive 对象是不是等于 null,第一次运行当然是等于 null 了,于是会调用 scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给 mActive 对象,然后调用 THREAD_POOL_EXECUTOR 去执行取出的取出的 Runnable 对象。之后如何又有新的任务被执行,同样还会调用 offer()方法将传入的 Runnable 添加到队列的尾部,但是再去给 mActive 对象做非空检查的时候就会发现 mActive 对象已经不再是 null 了,于是就不会再调用 scheduleNext()方法。

评论

发布
暂无评论
Android AsyncTask源码解析