配色: 字号:
Android AsyncTask工作原理
2016-10-20 | 阅:  转:  |  分享 
  
AndroidAsyncTask工作原理

AsyncTask能够适当简单的使用在UI线程,在没有任务线程和handler的情况下,这个类也允许执行后台操作并将结果显示在UI线程上。



AsyncTask的引入,我们在执行完耗时的后台任务后可以很方便的更新UI元素。相信大多数同学对AsyncTask的用法都已经很熟悉,那这里不在叙述他的基本用法了。



AsyncTask的实现原理是封装线程池和消息机制,我已经在自己的博客写过了线程池和消息机制的。感兴趣的同学可以去阅读下。



那么直接进入AsyncTask的源码分析,一起从源码的角度彻底理解。



先从AsyncTask的构造方法看起



/

Createsanewasynchronoustask.ThisconstructormustbeinvokedontheUIthread.

/

publicAsyncTask(){

mWorker=newWorkerRunnable(){

publicResultcall()throwsException{

mTaskInvoked.set(true);



Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

//noinspectionunchecked

Resultresult=doInBackground(mParams);

Binder.flushPendingCommands();

returnpostResult(result);

}

};



mFuture=newFutureTask(mWorker){

@Override

protectedvoiddone(){

try{

postResultIfNotInvoked(get());

}catch(InterruptedExceptione){

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

}catch(ExecutionExceptione){

thrownewRuntimeException("AnerroroccurredwhileexecutingdoInBackground()",

e.getCause());

}catch(CancellationExceptione){

postResultIfNotInvoked(null);

}

}

};

}



从方法的注释我们知道,创建一个异步任务,这个构造方法必须要在UI线程中调用。

AsyncTask的构造方法中,首先创建了一个WorkerRunnable对象并赋值给mWorker,那么这个WorkerRunnable是什么呢?



privatestaticabstractclassWorkerRunnableimplementsCallable{

Params[]mParams;

}



从上面的代码中可知道WorkerRunnable其实就是Callable。接着又创建了一个FutureTask对象,并将mWorker传进。AsyncTask的构造方法只是做了初始化的工作。

关于Callable和FutureTask我已经在线程池系列的博客中介绍过了。简单说就是FutureTask是Runnable的实现类,其中封装了Callable,当FutureTask的run方法被调用时,内部实际调用的是Callable的call方法。那么我们等下就寻找FutureTask的run方法在哪里被调用了,doInBackground方法也就是在那里开始被调用的。



那么接下来就是跟踪AsyncTask的execute方法了,源码如下:



publicfinalAsyncTaskexecute(Params...params){

returnexecuteOnExecutor(sDefaultExecutor,params);

}



内部调用了executeOnExecutor方法,这个方法顾名思义就是在线程池中执行,传入sDefaultExecutor和在参数params。

那么sDefaultExecutor又是什么呢?先不管。

我觉的刚开始分析execute方法不要一直在研究从四面八方出现的变量。因为让我们感到莫名其妙出现的变量实在太多了,我们一直去跟踪思路很容易会散了。稍后会分析sDefaultExecutor。

那么继续跟进executeOnExecutor方法看看:



publicfinalAsyncTaskexecuteOnExecutor(Executorexec,

Params...params){

if(mStatus!=Status.PENDING){

switch(mStatus){

caseRUNNING:

thrownewIllegalStateException("Cannotexecutetask:"

+"thetaskisalreadyrunning.");

caseFINISHED:

thrownewIllegalStateException("Cannotexecutetask:"

+"thetaskhasalreadybeenexecuted"

+"(ataskcanbeexecutedonlyonce)");

}

}



mStatus=Status.RUNNING;



onPreExecute();



mWorker.mParams=params;

exec.execute(mFuture);



returnthis;

}



关于Status是一个枚举类型,他有三种状态:PENDING,RUNNING,FINISHED,分别标明了当前AsyncTask的状态。当然初始化的时候是PENDING准备就绪状态,从



privatevolatileStatusmStatus=Status.PENDING;



这行代码也可以看出。那么继续往下看这个executeOnExecutor方法,之后会将Status的状态设置为RUNNING正在运行状态。AsyncTask如果在RUNNING,FINISHED状态执行executeOnExecutor方法就会抛出异常了,也表明一个AsyncTask实例只能执行一次任务。接着就会回调onPreExecute方法,所以我们可以重写onPreExecute这个方法做一些准备工作。



好,重头戏要开始了。



exec.execute(mFuture);



exec调用了execute方法并将mFuture(FutureTask对象)传入,之前我们已经在构造方法中初始化了FutureTask对象并赋值给mFuture。不过还需要知道exec,回到最初,我们是在AsyncTask的execute方法里,将sDefaultExecutor传了进来executeOnExecutor方法,所以exec就是sDefaultExecutor。

那么就需要知道sDefaultExecutor的execute方法里的内部实现是怎么样的,才能解开我们心中的疑惑了。

现在才是搞明白sDefaultExecutor是什么的正确时机,那我们就先来瞧瞧sDefaultExecutor是什么先?



/

An{@linkExecutor}thatexecutestasksoneatatimeinserial

order.Thisserializationisglobaltoaparticularprocess.

/

publicstaticfinalExecutorSERIAL_EXECUTOR=newSerialExecutor();



privatestaticvolatileExecutorsDefaultExecutor=SERIAL_EXECUTOR;



由此可知道,sDefaultExecutor是SerialExecutor的实例,从名字可以看出SerialExecutor是一个串行执行任务的线程池。我在Android中的线程池(二)那篇博客中也详细讲了各种线程池。



那就来看看SerialExecutor的源码:



privatestaticclassSerialExecutorimplementsExecutor{

finalArrayDequemTasks=newArrayDeque();

RunnablemActive;



publicsynchronizedvoidexecute(finalRunnabler){

mTasks.offer(newRunnable(){

publicvoidrun(){

try{

r.run();

}finally{

scheduleNext();

}

}

});

if(mActive==null){

scheduleNext();

}

}



protectedsynchronizedvoidscheduleNext(){

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

THREAD_POOL_EXECUTOR.execute(mActive);

}

}

}



SerialExecutor确实是一个线程池,很重要。这段代码也是我觉得最不好理解的。里面实现了Executor接口的execute方法。

SerialExecutor的execute方法内部,首先向ArrayDeque队列中提交一个Runnable对象,在其run方法里调用r的run方法,r就是mFuture。



publicvoidrun(){

try{

r.run();//等价于mFuture.run()

}finally{

scheduleNext();

}

}



有点绕,但容易理解的。其实就是这个run方法运行时,就调用FutureTask的run方法嘛。



当第一次执行execute方法,mActive一定是为空的,那么就会通过if(mActive==null)的判断调用到scheduleNext方法。

可以看到scheduleNext这个方法从队列的头部取出一个元素并赋值给mActive,如果mActive不为空,即队列里有任务。就调用THREAD_POOL_EXECUTOR的execute方法将mActive传进去执行任务。mActive经过第一次赋值之后就不为空了。之后就会进入finally块。也就是说scheduleNext还是会被调用。这样后续的任务又得到了处理。



总结一下:可以这样理解,假设我有10个AsyncTask任务,全部进入队列,肯定会有其中一个AsyncTask任务是第一个执行的吧,当第一个AsyncTask任务执行完毕,就会进入finally块,调用scheduleNext方法继续取出队列中的任务,这样下一个任务又得到了处理。

只有当一个任务执行完毕了,下一个任务才会执行。所以为什么说这种线程池是串行执行任务。



AsyncTask有两个线程池的,SerialExecutor和THREAD_POOL_EXECUTOR。前者负责任务的排队,后者用于任务的执行。THREAD_POOL_EXECUTOR的配置如下:



/

An{@linkExecutor}thatcanbeusedtoexecutetasksinparallel.

/

publicstaticfinalExecutorTHREAD_POOL_EXECUTOR

=newThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,

TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);



这些核心线程数,最大线程数,任务队列等参数如下:



privatestaticfinalintCPU_COUNT=Runtime.getRuntime().availableProcessors();

privatestaticfinalintCORE_POOL_SIZE=CPU_COUNT+1;

privatestaticfinalintMAXIMUM_POOL_SIZE=CPU_COUNT2+1;

privatestaticfinalintKEEP_ALIVE=1;

privatestaticfinalBlockingQueuesPoolWorkQueue=

newLinkedBlockingQueue(128);



那我们跟进THREAD_POOL_EXECUTOR的execute方法,看看是怎样执行任务的。



publicvoidexecute(Runnablecommand){

if(command==null)

thrownewNullPointerException();



intc=ctl.get();

if(workerCountOf(c)
if(addWorker(command,true))

return;

c=ctl.get();

}

if(isRunning(c)&&workQueue.offer(command)){

intrecheck=ctl.get();

if(!isRunning(recheck)&&remove(command))

reject(command);

elseif(workerCountOf(recheck)==0)

addWorker(null,false);

}

elseif(!addWorker(command,false))

reject(command);

}



这里的流程,我也在之前的博客分析过了,不过这里为了更好阅读,我还是在分析一遍吧。这个execute方法里的逻辑不简单,所以我们要结合AsyncTask使用到的线程池的参数配置,抓主要信息阅读。



那主要是将mFuture加入将要执行的队列中,我们只需要跟进addWorker方法即可。



privatebooleanaddWorker(RunnablefirstTask,booleancore){

retry:



//代码省略



Workerw=null;

try{

w=newWorker(firstTask);

finalThreadt=w.thread;

if(t!=null){

//代码省略

workers.add(w);

ints=workers.size();

if(s>largestPoolSize)

largestPoolSize=s;

workerAdded=true;

}

}finally{

mainLock.unlock();

}

if(workerAdded){

t.start();

workerStarted=true;

}

}

}finally{

if(!workerStarted)

addWorkerFailed(w);

}

returnworkerStarted;

}



创建一个Worker对象并对传进的FutureTask进行包装,从Worker对象中取出线程并赋值给t,然后将Worker对象添加进工作线程队列等待执行,最后线程t调用start方法。即调用Worker里的run方法,在Worker里的run方法里又调用了runWorker方法,如下:



finalvoidrunWorker(Workerw){

Threadwt=Thread.currentThread();

Runnabletask=w.firstTask;

w.firstTask=null;

w.unlock();//allowinterrupts

booleancompletedAbruptly=true;

try{

//代码省略

try{

task.run();

}catch(RuntimeExceptionx){

thrown=x;throwx;

}

//代码省略

}finally{

processWorkerExit(w,completedAbruptly);

}

}



取出刚才在Worker对象中封装的FutureTask对象,并赋值给task,最后task调用run方法。这样mFuture的run方法就得到了调用。



在FutureTask对象run方法里会调用Callable的call方法,FutureTask对象run方法如下:



publicvoidrun(){

//代码省略

try{

Callablec=callable;

if(c!=null&&state==NEW){

Vresult;

booleanran;

try{

result=c.call();

ran=true;

}

//代码省略

}



这个Callable也就是我们最初在AsyncTask构造方法中创建的WorkerRunnable对象mWorker。现在我们可以看到mWorker的call方法了



publicResultcall()throwsException{

mTaskInvoked.set(true);



Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

//noinspectionunwww.shanxiwang.netchecked

Resultresult=doInBackground(mParams);

Binder.flushPendingCommands();

returnpostResult(result);

}



饶了地球一圈,终于执行到了doInBackground方法了。这里也可以证实,onPreExecute方法是第一个被调用的,而第二个回调的是doInBackground方法。

那继续前进吧,将doInBackground返回的结果传进了postResult方法,postResult方法源码如下:



privateResultpostResult(Resultresult){

@SuppressWarnings("unchecked")

Messagemessage=getHandler().obtainMessage(MESSAGE_POST_RESULT,

newAsyncTaskResult(this,result));

message.sendToTarget();

returnresult;

}



终于看到消息机制了,getHandler方法获取到InternalHandler对象,并将MESSAGE_POST_RESULT字段和一个包装了result的AsyncTaskResult对象发送给handleMessage方法接收。



privatestaticclassAsyncTaskResult{

finalAsyncTaskmTask;

finalData[]mData;



AsyncTaskResult(AsyncTasktask,Data...data){

mTask=task;

mData=data;

}

}



AsyncTaskResult是一个用于封装AsyncTask实例和结果集的内部类。



那么我们现在直接到handleMessage方法一看究竟。



privatestaticclassInternalHandlerextendsHandler{

publicInternalHandler(){

super(Looper.getMainLooper());

}



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

@Override

publicvoidhandleMessage(Messagemsg){

AsyncTaskResultresult=(AsyncTaskResult)msg.obj;

switch(msg.what){

caseMESSAGE_POST_RESULT:

//Thereisonlyoneresult

result.mTask.finish(result.mData[0]);

break;

caseMESSAGE_POST_PROGRESS:

result.mTask.onProgressUpdate(result.mData);

break;

}

}

}



在InternalHandler的构造方法中,获取到UI线程的Looper对象,这就是为什么AsyncTask要在主线程创建,因为我要关联主线程的Looper,消息队列。



刚才发的消息是MESSAGE_POST_RESULT,那我们看到这个分支即可,这里调用了AsyncTask的finish方法并传进result。



privatevoidfinish(Resultresult){

if(isCancelled()){

onCancelled(result);

}else{

onPostExecute(result);

}

mStatus=Status.FINISHED;

}



在一路顺畅不抛异常的正常情况下,AsyncTask没有被取消,会将result传入onPostExecute方法,执行完onPostExecute后,整个AsyncTask的生命周期也就结束了。



还有一个知识点没讲到的是,在doInBackground方法中调用publishProgress方法,可以在onProgressUpdate方法中更新UI,比如更新进度条的进度。



protectedfinalvoidpublishProgress(Progress...values){

if(!isCancelled()){

getHandler().obtainMessage(MESSAGE_POST_PROGRESS,

newAsyncTaskResult(this,values)).sendToTarget();

}

}



其实这个publishProgress方法也很简单,就是将当前的进度值values发送到主线程。



如果我们想要并行执行任务,可以调用AsyncTask的executeOnExecutor方法。如下:



newAsyncTask(){



@Override

protectedVoiddoInBackground(Void...params){

returnnull;

}



}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,null);



其实很好理解的。直接执行executeOnExecutor方法,传入AsyncTask.THREAD_POOL_EXECUTOR线程池。这样就绕过了SerialExecutor任务排队的过程。



以上就是根据源码的角度分析的AsyncTask的工作原理。

献花(0)
+1
(本文系网络学习天...首藏)