zoukankan      html  css  js  c++  java
  • 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()方法。            


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


  • 相关阅读:
    C++基类的析构函数定义为虚函数的原因
    android的学习网站
    QT显示url图片
    Ubuntu安装JDK
    linux下打包压缩和解压命令
    嵌入式目录
    QT pri 文件的作用
    QT pro文件详细写法+实例
    Computer(树的直径做法)
    树的直径
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7063224.html
Copyright © 2011-2022 走看看