关闭

AsyncTask源码解析

标签: AsyncTaskandroid源码解析android
424人阅读 评论(10) 收藏 举报
分类:

 Android   AsyncTask源码解析

作者:涂臻宇  创作时间:2015-07-10

简介:

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask。

    Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。

    为了简化操作,Android1.5提供了工具类android.os.AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。

    先来看看AsyncTask的定义:

public abstract class AsyncTask<Params, Progress, Result> {  

三种泛型类型分别代表“启动任务执行的输入参数”、“后台任务执行的进度”、“后台计算结果的类型”。在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。

一个异步任务的执行一般包括以下几个步骤:

1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

在使用的时候,有几点需要格外注意:

1.异步任务的实例必须在UI线程中创建。

2.execute(Params... params)方法必须在UI线程中调用。

3.不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。

4.不能在doInBackground(Params... params)中更改UI组件的信息。

5.一个任务实例只能执行一次,如果执行第二次将会抛出异常。

注:这段简介是借鉴的,废话不多说,来看下面的源码吧。


主要思想:

AsyncTask有两个重要的思想:

1.利用线程池对多线程进行管理,如果有必要请先了解线程池;

2.对子线程和Hnadler的交互进行封装。


下文将针对这两点进行分析。

1.AsyncTask中队线程池的利用:

    首先,什么是线程池?线程池的思想是在系统启动时创建大量空闲的线程,当程序有耗时的任务需要执行时,就把任务封装在一个Runnable或者CallAble对象,并把该对象传给线程池,线程池就会启动一个线程来执行它们的run() 方法或者call()方法,注意:当run() 方法或者call()方法执行完毕后,该线程并不会死亡,而是再次返回线程池中再次处于空闲状态,等待执行下一个对象的run() 方法或者call()方法。

    当程序需要创建大量生存期很短的线程时,线程池是种很好的选择,利用线程池避免了创建大量线程的成本。

    AsyncTask不仅利用了线程池,而且对线程池进行了简单的封装,使得线程池种的任务能按”先进先出”的顺序执行。

    注意:为了方便,把Runnable或者Callable统称为“任务”。

AsyncTask类中有如下几个类变量,注意,是类变量,是所有对象共享的:

 

  private static final int CORE_POOL_SIZE = 5; //核心线程数5;           

    private static final int MAXIMUM_POOL_SIZE = 128;//最大线程数128 

    private static final int KEEP_ALIVE = 1;                           

 

上述几个变量不需赘述了吧。看下面几个:

 

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

   public static final Executor  THREAD_POOL_EXECUTOR=new ThreadPoolExecutor(CORE_POOL_SIZE, 

                                                                             MAXIMUM_POOL_SIZE, KEEP_ALIVE,

                                                                             TimeUnit.SECONDS,

                                                                             sPoolWorkQueue, 

                                                                             sThreadFactory);

 

    上面几个变量是不是看得有些复杂?没关系,上面那些变量总结来说,做了一件事:创建了一个线程池对象THREAD_POOL_EXECUTOR。所有AsyncTask实例所需要执行的耗时操作最终由该线程池对象负责执行

        再看接下来的几个变量:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

       

    好吧,这里又创建了一个线程池对象SERIAL_EXECUTOR,为什么这里再创建一个线程池对象呢?因为线程池对象THREAD_POOL_EXECUTOR只是单纯地把任务放进线程池里执行,但这里为了管理方便,需要对线程池对象THREAD_POOL_EXECUTOR进行一定处理,可以看到线程池对象SERIAL_EXECUTOR是类SerialExecutor的实例,就是该类对线程池对象THREAD_POOL_EXECUTOR做了处理,请看该类:

        首先,该类是AsyncTask里的内部类:

 

 

    可以看到该类继承于Exector类,是线程池的子类,这个类里有一个成员变量:mTasks,mTasks是个双端队列,所有未执行的任务都会被按照先进先出的顺序放入该队列。当新任务提交时,即调用execute(final Runnable r) 方法,可以看到,任务r又被封装成一个新的Runnable对象,该对象会被提交到队列mTasks的队尾。在239行可看出,新的Runnable对象在执行完r的run方法后在finally块里执行了scheduleNext()方法,即,每个任务干完自己的活后都会执行scheduleNext()。

scheduleNext()方法很简单,就是从队列mTasks中取出一个任务并提交给THREAD_POOL_EXECUTOR并执行。

 

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;          

 

    sDefaultExecutor 只是单纯地指向SERIAL_EXECUTOR,在后面可以看到,AsyncTask里都是使用sDefaultExecutor ,而未直接使用SERIAL_EXECUTOR。所以这个变量不用纠结。

 

    总结一下:所有任务首先会被提交给SERIAL_EXECUTOR这个线程池,然后SERIAL_EXECUTOR将新任务提交给队列mTasks,然后又不断地从队列头取出任务,提交给THREAD_POOL_EXECUTOR去执行,每执行完一个任务后就自动从队列中再取出任务去执行,周而复始。

所以真正执行任务的是线程池THREAD_POOL_EXECUTOR;

SERIAL_EXECUTOR只是把任务进行排队处理。

    AsyncTask里利用线程池对任务的处理大概就是这样,所以上文一直提到任务,然而对于用户来说,我们并没有创建一个Runnable或者Callable对象,我们只是简单地把耗时的操作放在doInBackground()方法里,哪来的“任务”呢?

    看AsyncTask里的两个变量,注意,这两个变量不是类变量,是每个对象私有的:

    private final WorkerRunnable<Params, Result> mWorker;//

    private final FutureTask<Result> mFuture;//

    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {

        Params[] mParams;

    }

    解释下:WorkerRunnable<Params, Result> 继承于Callable类,如果对于Callable类不熟悉,就把它当作有返回值的Runnable。至于FutureTask对象mFuture,就是用于接收mWorker的返回值的对象。

    在这里,mWorker就是所说的“任务”。

    看看AsyncTask的构造方法:

 

    构造方法里,分别将mWorker和mFuture 指向了两个新对象,可以看到新的WorkerRunnable对象里在call()方法里面第295行执行了doInBackground()方法,所以我们重写的doInBackground方法最终是被mWorker所指向的这个对象所调用,所以mWorker就是我上文说的“任务”,这个mWorker最终会被提交到线程池THREAD_POOL_EXECUTOR里执行,call()方法相当于Runnable的run()方法,所以mWorker的call方法最终会在THREAD_POOL_EXECUTOR一条新线程里执行,使得doInBackground()方法能在新线程里执行!

    至于mFuture ,可暂时忽略不计。

    现在,问题是,什么时候会把mWorker方法提交给线程池去执行呢?当然是AsyncTask的execute()方法了!如下:

 

    execute()只是简单地调用了executeOnExecutor方法,并把sDefaultExecutor, params作为参数传过去,params就是我们执行异步任务时给的参数列表,sDefaultExecutor就是上文所说的经过处理的线程池。

    接下来看看executeOnExecutor利用这两个变量做了什么?


    首先,修改了异步任务的状态,这不是重点;

    看第594行,先调用了onPreExecute()!

    在第596~597行,就真相大白了:

    mWorker得到了参数params,然后把mWorker提交给线程池sDefaultExecutor执行了!至此,就做到了在新线程里执行doInBackground()方法!

2.对子线程和Hnadler的交互进行封装:

   在第一部分里,笔者讲到doInBackground()方法会在mWorker的call()方法里被调用,而随着mWorker被提交到线程池里执行,doInBackground()方法也会在新新线程中被执行。这解决了一个问题:在新线程里执行了耗时操作,还有一个问题,如何让新线程与主线程进行交互?

这里,AsyncTask依旧用到了Handler,AsyncTask里内置了一个Handler对象:

   private static final InternalHandler sHandler = new InternalHandler();

   InternalHandler 是定义在AsyncTask的Handler的子类,是AsyncTask的内部类。

   先不管这个这个sHandler 做了什么,先看mWorker的call()方法:

 

   可以看出,在该方法里面doInBackground()执行后,其返回值被作为参数传递给postResult()方法,并执行了postResult()。看看postResult()方法:


   可以看到,在这个方法里,把result(即doInBackground()方法的返回值)及AsyncTask对象本身传递给一个AsyncTaskResult<Result>类的构造函数,生成一个AsyncTaskResult<Result>实例。然后把AsyncTaskResult<Result>实例封装成Message传递给sHandler。

   所以,postResult()实现了把Result封装成Message发送给了Handler!

   这个AsyncTaskResult类很简单,就是对AsyncTask执行后得到的结果进行记录:

 

   接下来sHandler如何处理postResult()发来的Message:

 

   很简单吧,从消息里取出AsyncTaskResult对象,并且由于sHandler是类变量,是所有AsyncTask共享的,所以为了知道发送来的Message里的result该由哪个AsyncTask处理,AsyncTaskResult就记录了对应的AsyncTask在mTask中,并且如果该消息类型是“结果”,则调用对应mTask(即AsyncTask)的finish()方法,看看finish()方法:

 

   可以看出,如果该AsyncTask没被取消了任务,则最终调用了我们复写的onPostExecute(result),而且可看出onPostExecute(result)在主线程执行!

    同理,如果在doInBackground()里调用了publishProgress方法,根据以下代码可知:

 

   sHandler里第二个case语句里的代码会被执行,即调用了我们复写的onProgressUpdate()方法!

   至此,整个调用流程就走完了!

   总结一下:在mWorker的call()方法中,在新线程中执行doInBackground()后,postResult()会把doInBackground()的返回结果封装成Message并且发送给所有AsyncTask对象共享的Handlerd对象,该Handler对象会取出每个Message中的结果,并让对应的AsyncTask在主线程里最终执行onPostExecute()方法。            


  后记:这是笔者第一次写博客,语文不好,望大家见谅。


0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:9748次
    • 积分:254
    • 等级:
    • 排名:千里之外
    • 原创:14篇
    • 转载:0篇
    • 译文:1篇
    • 评论:14条
    文章分类
    最新评论