内容简介:AsyncTask是android为我们提供执行异步任务的一个轻量的类,可以用来处理耗时操作,并且能够很方便的将执行结果返回给主线程。本篇文章将会通过源码分析来介绍AsyncTask的内部实现原理。AsyncTask里面几个重要的成员变量变量分别为:在分析AsyncTask之前我们先看看他的构造,我们在使用
AsyncTask是android为我们提供执行异步任务的一个轻量的类,可以用来处理耗时操作,并且能够很方便的将执行结果返回给主线程。本篇文章将会通过源码分析来介绍AsyncTask的内部实现原理。
目录
- 重要的成员变量
- AsyncTask构造分析
- 两个线程池
- 图解AsyncTask执行过程
- 执行结果是如何被传递到主线程
- onProgressUpdate()是什么时候调用
1. 重要的成员变量
AsyncTask里面几个重要的成员变量变量分别为:
名称 | 作用 | 创建 | 调用 | 备注 |
---|---|---|---|---|
THREAD_POOL_EXECUTOR | 真正执行任务的线程池 | 在静态代码块中被创建 | 在SerialExecutor线程池的scheduleNext方法中被调用 | 该线程成池的核心线程数量是根据手机cup核数-1确定的 |
sDefaultExecutor | 内部创建队列用于储存异步任务 | 创建类的成员变量的时候被创建 | 在AsyncTask的execute()中被作为参数传递 | SerialExecutor类的scheduleNext方法中会将任务添加到THREAD_POOL_EXECUTOR线程池中执行 |
mWorker | 任务最终执行方法,其内部的call方法会调用doInBackground()方法 | 在AsyncTask有参构造中创建 | WorkerRunnable在FutureTask的run方法中被调用该类的call方法 | 其继承自Callable方法,一般配合FutureTask使用 |
mFuture | 在其内部会调用mWorker的call方法来执行任务 | 在AsyncTask有参构造中创建 | FutureTask在SerialExecutor类的execute方法中被调用 | 该成员变量被AsyncTask的executeOnExecutor()中传递到SerialExecutor中 |
sHandler | 用于将在结果返回到主线程 | 在AsyncTask有参构造中通过调用getMainHandler来创建 | 在postResult()中通过复用Message来调用 | InternalHandler类的Looper是主线程的Looper |
2. AsyncTask构造分析
在分析AsyncTask之前我们先看看他的构造,我们在使用 AsyncTask
经常使用空参构造的方式来创建该对象,这个构造方法内部会调用他的有参构造。首先有参会先根据是否有Looper来创建Handler。如果传入的Looper为空或者传入的Looper不是主线程的Looper,则调用 getMainHandler()
来创建Handler;如果是主线程的Looper则以此Looper重新new一个Handler。当Handler创建完毕后然后在以次创建 WorkerRunnable
和 FutureTask
。下面为AsyncTask构造源码:
public AsyncTask(@Nullable Looper callbackLooper) { //创建Hanlder mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler() : new Handler(callbackLooper); mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { //将进程设置成标准后台进程 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //获取异步执行结果 result = doInBackground(mParams); //将进程中未执行的命令一并送往cup处理 Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { //将处理结果返回到主线程 postResult(result); } return result; } }; //FutureTask间接调用了WorkerRunnable方法的call方法 //来获取执行结果 mFuture = new FutureTask<Result>(mWorker) { @Override protected 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); } } }; } 复制代码
从源码中我们可以知道,mHandler实际上是 InternalHandler
, mWorker
内部的call()方法会调用 doInBackground
,try块不管执行结果如何,都会调用 postResult()
来调用Hanlder发送消息,通知主线程最Ui更新操作。先有一个问题,call()方法是在哪里会被调用呢?其实是在mFuture内部的run()方法中调用 mWorker
他的call方法。具体代码读者可以自行查找项目源码,这里就不多说了。上面提到的mWorker、mFuture会在 execute()
方法中被调用和传递,execute()是用于配置和启动任务的方法,下面为该方法的部分代码。
/** *在主线程中执行 *可传入一个或多个参数 */ @MainThread public final AsyncTask。<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } 复制代码
3. 两个线程池
executeOnExecutor(sDefaultExecutor, params);
方法将参数params和 sDefaultExecutor
传入该方法中,并返回一个AsyncTask。这个params我们知道它是我们传进来的参数,但是sDefaultExecutor是什么呢?它是一个线程池,是一个类的成员变量。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 复制代码
既然我们知道sDefaultExecutor是一个线程池,也就是 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() { //mTask.pll()删除队列中的第一个元素,并返回该元素的值 if ((mActive = mTasks.poll()) != null) { //调用线程池执行异步 THREAD_POOL_EXECUTOR.execute(mActive); } } } 复制代码
从上面的代码我们可以知道,SerialExecutor类中创建一个双端队列 ArrayDeque
, 用于储存异步任务。他还有 execute()
和 scheduleNext()
方法,execute()内部调用了 mTasks.offer
用于将传入的异步任务添加到队列中,然后在调用 scheduleNext()方法。scheduleNext()方法调用 mTask.poll()
方法取出并删除第一个元素,最后将取出的元素放到线程池中。不知道读者有没有发现AsyncTask内部其实是有两个线程池 SerialExecutor
和 THREAD_POOL_EXECUTOR
,其中SerialExecutor线程池主要是用于将任务添加到队列中,而任务真正的执行是在THREAD_POOL_EXECUTOR线程池中。
4. 图解AsyncTask执行过程
要想知道执行结果是如何被传递到线程中,我们先搞明白AsyncTask的执行过程。其实读者从上面的内容中或许能改猜到它的大概执行过程。其实它的执行过程也不复杂我们可以结果下面这张图进行分析:
我们在使用AsyncTask的时候会先创建对象,然后调用execute()方法传入参数执行任务:
//创建AcyncTask封装类 TestAsyncTask asyncTask = new TestAsyncTask(); //传入参数,执行任务 asyncTask.execute(5,6,7); 复制代码
我们在通过上面操作执行任务的时候,其实AsyncTask内部做了一下几个操作:
executeOnExecutor()
结合上面的执行流程图我们知道,在经过上面7个步骤异步任务一个一个的在线程池中被完成。既然我们知道了AsyncTask的大致执行过程,那么它是如何将执行结果返回到主线程呢?下面我们将会来分析。
5. 执行结果是如何被传递到主线程
我们知道 doInBackground()
函数是我们的任务具体执行函数。这个函数是在WorkerRunnable的call()函数中被调用,从上面的执行过程介绍中我们知道call()方法是在FutureTask的run方法执行的时候被调用的。当call()方法在执行完doInBackground()方法得到结果后,会将该结果传递给 postResult()
方法:
private Result postResult(Result result) { //obtainMessage方法是从Message池中获取一个Message对象,避免重复创建。 Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); //发送消息 message.sendToTarget(); return result; } 复制代码
postResult()方法内代码也很简单,首先它会通过Hanlder(注:从文章开始部分我们可以知道,这个Handler的Looper是主线程的Looper)在消息队列中获取一个Message对象,然后将结果和定义的标记包装到Massage中,最后在通过Message对象调用sendToTarget()将消息发出。既然消息发送出去了,那么消息是在哪里执行呢?答案是:在 InternalHandler
类中的 handleMessage()
中被执行。why?因为getHandler()获取的是Hanlder是我们在文章开始介绍的构造函数中被getMainHandler()赋值的 mHandler
,而getMainHandler()中返回的就是InternalHandler。既然我们知道了消息在哪里被处理,那么我们可以看一看它的具体处理逻辑:
public void handleMessage(Message msg) { AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj; switch (msg.what) { case MESSAGE_POST_RESULT: // There is only one result result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } 复制代码
handleMessage()内部有两个判断,如果标识是 MESSAGE_POST_RESULT
则将结果传递给 finish()
方法。如果标识是 MESSAGE_POST_PROGRESS
则调用 onProgressUpdate()
用于更新进度。下面我们先看finish()方法的源码:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = AsyncTask.Status.FINISHED; } 复制代码
finish()方法会判断是否取消了该任务,如果用户调用了 cancel()
函数那么 isCancelled()
返回的就是true,当用户取消了任务那么将会回调onCancelled(result)函数而onPostExecute()则不会调用,反之则会调用。
6. onProgressUpdate()是什么时候调用
在分析handleMessage()方法的时候我们留了一个小尾巴, MESSAGE_POST_PROGRESS
这个标记消息在什么时候发出的?在回答这个问题之前,我们先回忆一下我们在使用 doInBackground()
的时候,是否有在其内部调用 publishProgress()
函数来更新进入?回忆到这里答案就很明显了:通过Handler发生更新进度消息的操作是在 publishProgress()
函数中完成的。下面为该函数的源码:
@WorkerThread protected final void publishProgress(Progress... values) { //如果任务没有取消,则发生消息更新进度 if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } } 复制代码
从上面的源码我们可以知道,更新进度的消息是在子线程中发送的,如果该任务没有被取消那么就可以发现消息。这种通过复用Message对象发送信息的方式对性能上有起到优化的作用。读者可以在文章结尾的参考链接中找到相关的介绍,笔者就不介绍了。
以上所述就是小编给大家介绍的《AsyncTask源码解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ReactNative源码解析-初识源码
- Spring源码系列:BeanDefinition源码解析
- Spring源码分析:AOP源码解析(下篇)
- Spring源码分析:AOP源码解析(上篇)
- 注册中心 Eureka 源码解析 —— EndPoint 与 解析器
- 新一代Json解析库Moshi源码解析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
iOS游戏编程之从零开始
李华明 / 2013-2 / 59.00元
《iOS游戏编程之从零开始:Cocos2d-x与cocos2d引擎游戏开发》是作者继《android游戏编程之从零开始》热销之后编写的又一本、基于cocos2d—x2.x和cocos2d—iphone版本,讲述ios平台游戏开发的新作。《iOS游戏编程之从零开始:Cocos2d-x与cocos2d引擎游戏开发》分为两个部分共11章,内容主要包括cocos2d—x引擎游戏开发的基础,常用的类、方法及......一起来看看 《iOS游戏编程之从零开始》 这本书的介绍吧!