AsyncTask源代码解析

 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()方法非常easy,就是从队列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类非常easy,就是对AsyncTask运行后得到的结果进行记录:

 

   接下来sHandler怎样处理postResult()发来的Message:

 

   非常easy吧,从消息里取出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()方法。            


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


原文地址:https://www.cnblogs.com/brucemengbm/p/7063224.html