AsyncTask源码分析

来源:互联网 发布:天津工业大学网络教学 编辑:程序博客网 时间:2024/06/05 02:56

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

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

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. }  

public AsyncTask() {

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

    public Result call()throws Exception {

      mTaskInvoked.set(true);

      Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

      return postResult(doInBackground(mParams));

    }

  };

  mFuture =new FutureTask<Result>(mWorker) {

    @Override

    protectedvoid done() {

      try {

        final Result result = get();

        postResultIfNotInvoked(result);

      }catch (InterruptedException e) {

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

      }catch (ExecutionException e) {

        thrownew RuntimeException("An error occured while executing doInBackground()",

            e.getCause());

      }catch (CancellationException e) {

        postResultIfNotInvoked(null);

      }catch (Throwable t) {

        thrownew RuntimeException("An error occured while executing "

            +"doInBackground()", t);

      }

    }

  };

}

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

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


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

2.        return  executeOnExecutor(sDefaultExecutor, params);  

3.   }  

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

  return executeOnExecutor(sDefaultExecutor,params);

}

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


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. }  

public final AsyncTask<Params, Progress, Result>executeOnExecutor(Executor exec,

    Params...params) {

  if (mStatus != Status.PENDING) {

    switch (mStatus) {

      case RUNNING:

        thrownew IllegalStateException("Cannot execute task:"

            +"the task is already running.");

      case FINISHED:

        thrownew IllegalStateException("Cannot execute task:"

            +"the task has already been executed "

            +"(atask can be executed only once)");

    }

  }

  mStatus =Status.RUNNING;

  onPreExecute();

  mWorker.mParams=params;

  exec.execute(mFuture);

  returnthis;

}

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


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

2.   ……  

3.   private   static   volatile  Executor sDefaultExecutor = SERIAL_EXECUTOR;  

publicstaticfinal Executor SERIAL_EXECUTOR =new SerialExecutor();

……

privatestaticvolatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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


1.   private   static   class  SerialExecutor  implements  Executor {  

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

3.       Runnable mActive;  

4.     

5.        public   synchronized   void  execute( final  Runnable r) {  

6.           mTasks.offer( new  Runnable() {  

7.                public   void  run() {  

8.                    try  {  

9.                       r.run();  

10.                 }  finally  {  

11.                     scheduleNext();  

12.                 }  

13.             }  

14.         });  

15.          if  (mActive ==  null ) {  

16.             scheduleNext();  

17.         }  

18.     }  

19.   

20.      protected   synchronized   void  scheduleNext() {  

21.          if  ((mActive = mTasks.poll()) !=  null ) {  

22.             THREAD_POOL_EXECUTOR.execute(mActive);  

23.         }  

24.     }  

25. }  

privatestaticclass SerialExecutorimplements Executor {

  final ArrayDeque<Runnable> mTasks =new ArrayDeque<Runnable>();

  RunnablemActive;

 

  publicsynchronizedvoid execute(final Runnable r) {

    mTasks.offer(new Runnable() {

      publicvoid run() {

        try {

          r.run();

        }finally {

          scheduleNext();

        }

      }

    });

    if (mActive ==null) {

      scheduleNext();

    }

  }

 

  protectedsynchronizedvoid scheduleNext(){

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

      THREAD_POOL_EXECUTOR.execute(mActive);

    }

  }

}

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


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. }  

void innerRun() {

  if (!compareAndSetState(READY, RUNNING))

    return;

  runner =Thread.currentThread();

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

    V result;

    try {

      result =callable.call();

    }catch (Throwable ex) {

      setException(ex);

      return;

    }

    set(result);

  }else {

    releaseShared(0);// cancel

  }

}

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


1.   public  Result call()  throws  Exception {  

2.       mTaskInvoked.set( true );  

3.       Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  

4.        return  postResult(doInBackground(mParams));  

5.   }  

public Result call() throwsException {

  mTaskInvoked.set(true);

  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

  returnpostResult(doInBackground(mParams));

}

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


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.   }  

private Result postResult(Result result) {

  Messagemessage = sHandler.obtainMessage(MESSAGE_POST_RESULT,

      new AsyncTaskResult<Result>(this, result));

  message.sendToTarget();

  return result;

}

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


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. }  

privatestaticclass InternalHandlerextends Handler {

  @SuppressWarnings({"unchecked","RawUseOfParameterizedType"})

  @Override

  publicvoid handleMessage(Message msg) {

    AsyncTaskResultresult = (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;

    }

  }

}

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


1.   private   void  finish(Result result) {  

2.        if  (isCancelled()) {  

3.           onCancelled(result);  

4.       }  else  {  

5.           onPostExecute(result);  

6.       }  

7.       mStatus = Status.FINISHED;  

8.   }  

privatevoid finish(Resultresult) {

  if (isCancelled()) {

    onCancelled(result);

  }else {

    onPostExecute(result);

  }

  mStatus =Status.FINISHED;

}

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

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


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.   }  

protectedfinalvoid publishProgress(Progress... values) {

  if (!isCancelled()) {

    sHandler.obtainMessage(MESSAGE_POST_PROGRESS,

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

  }

}

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

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

关于AsyncTask你所不知道的秘密

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


1.   private   static   class  SerialExecutor  implements  Executor {  

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

3.       Runnable mActive;  

4.     

5.        public   synchronized   void  execute( final  Runnable r) {  

6.           mTasks.offer( new  Runnable() {  

7.                public   void  run() {  

8.                    try  {  

9.                       r.run();  

10.                 }  finally  {  

11.                     scheduleNext();  

12.                 }  

13.             }  

14.         });  

15.          if  (mActive ==  null ) {  

16.             scheduleNext();  

17.         }  

18.     }  

19.   

20.      protected   synchronized   void  scheduleNext() {  

21.          if  ((mActive = mTasks.poll()) !=  null ) {  

22.             THREAD_POOL_EXECUTOR.execute(mActive);  

23.         }  

24.     }  

25. }  

privatestaticclass SerialExecutorimplements Executor {

  final ArrayDeque<Runnable> mTasks =new ArrayDeque<Runnable>();

  RunnablemActive;

 

  publicsynchronizedvoid execute(final Runnable r) {

    mTasks.offer(new Runnable() {

      publicvoid run() {

        try {

          r.run();

        }finally {

          scheduleNext();

        }

      }

    });

    if (mActive ==null) {

      scheduleNext();

    }

  }

 

  protectedsynchronizedvoid scheduleNext(){

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

      THREAD_POOL_EXECUTOR.execute(mActive);

    }

  }

}

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

那么后面添加的任务岂不是永远得不到处理了?当然不是,看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。 Android照片墙应用实现,再多的图片也不怕崩溃  这篇文章中例子的运行结果也证实了这个结论。 

不过你可能还不知道,在Android 3.0之前是并没有SerialExecutor这个类的,那个时候是直接在AsyncTask中构建了一个sExecutor常量,并对线程池总大小,同一时刻能够运行的线程数做了规定,代码如下所示:


1.   private   static   final   int  CORE_POOL_SIZE =  5 ;  

2.   private   static   final   int  MAXIMUM_POOL_SIZE =  128 ;  

3.   private   static   final   int  KEEP_ALIVE =  10 ;  

4.   ……  

5.   private   static   final  ThreadPoolExecutor sExecutor =  new  ThreadPoolExecutor(CORE_POOL_SIZE,  

6.           MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);  

privatestaticfinalint CORE_POOL_SIZE =5;

privatestaticfinalint MAXIMUM_POOL_SIZE =128;

privatestaticfinalint KEEP_ALIVE =10;

……

privatestaticfinal ThreadPoolExecutor sExecutor =new ThreadPoolExecutor(CORE_POOL_SIZE,

        MAXIMUM_POOL_SIZE, KEEP_ALIVE,TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

可以看到,这里规定同一时刻能够运行的线程数为5个,线程池总大小为128。也就是说当我们启动了10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,当我们尝试去添加第129个任务时,程序就会崩溃。

因此在3.0版本中AsyncTask的改动还是挺大的,在3.0之前的AsyncTask可以同时有5个任务在执行,而3.0之后的AsyncTask同时只能有1个任务在执行。为什么升级之后可以同时执行的任务数反而变少了呢?这是因为更新后的AsyncTask已变得更加灵活,如果不想使用默认的线程池,还可以自由地进行配置。比如使用如下的代码来启动任务:


1.   Executor exec =  new  ThreadPoolExecutor( 15 ,  200 ,  10 ,  

2.           TimeUnit.SECONDS,  new  LinkedBlockingQueue<Runnable>());  

3.   new  DownloadTask().executeOnExecutor(exec);  

Executor exec = new ThreadPoolExecutor(15,200, 10,

    TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

new DownloadTask().executeOnExecutor(exec);

这样就可以使用我们自定义的一个Executor来执行任务,而不是使用SerialExecutor。上述代码的效果允许在同一时刻有15个任务正在执行,并且最多能够存储200个任务。

0 0
原创粉丝点击