Android 70 AsyncTask分析.docx
《Android 70 AsyncTask分析.docx》由会员分享,可在线阅读,更多相关《Android 70 AsyncTask分析.docx(16页珍藏版)》请在冰豆网上搜索。
Android70AsyncTask分析
Android7.0AsyncTask分析
1、基本定义
在分析AsyncTask的源码前,先来结合原生文档,回顾一下AsyncTask的定义。
AsyncTaskenablesproperandeasyuseoftheUIthread.
ThisclassallowstoperformbackgroundoperationsandpublishresultsontheUIthreadwithout
havingtomanipulatethreadsand/orhandlers.
AsyncTask主要用于帮助UI线程执行后台操作,并将执行结果递交给UI线程。
使用AsyncTask执行后台工作,可以简化UI线程的设计,避免在UI线程中操作额外的线程和Handler。
AsyncTasksshouldideallybeusedforshortoperations(afewsecondsatthemost.)
Ifyouneedtokeepthreadsrunningforlongperiodsoftime,itishighlyrecommendedyouusethevariousAPIs
providedbytheJava.util.concurrentpackagesuchasExecutor,ThreadPoolExecutorandFutureTask.
AsyncTask执行的后台操作不应该耗时过长,若需要利用线程执行耗时任务,最好使用并发库中的工具类。
Anasynchronoustaskisdefinedby3generictypes,calledParams,ProgressandResult.
Params,thetypeoftheparameterssenttothetaskuponexecution.
Progress,thetypeoftheprogressunitspublishedduringthebackgroundcomputation.
Result,thetypeoftheresultofthebackgroundcomputation.
Notalltypesarealwaysusedbyanasynchronoustask.
Tomarkatypeasunused,simplyusethetypeVoid.
AsyncTask是一个模板类,定义了3个模板参数,分别是Params、Progress和Result。
其中:
Params:
AsyncTask执行时的输入参数的类型;
Progress:
后台执行任务时,计算出的完成进度的数值类型;
Result:
AsyncTask执行完毕后的返回类型。
这些模板参数并不一定需要全部定义,对于不用的参数,指定其为Void即可,例如:
privateclassMyTaskextendsAsyncTask{...}
1
1
Whenanasynchronoustaskisexecuted,thetaskgoesthrough4steps:
AsyncTask定义了4个回调接口,分别为onPreExecute、doInBackground、onProgressUpdate和onPostExecute。
onPreExecute,invokedontheUIthreadbeforethetaskisexecuted.
Thisstepisnormallyusedtosetupthetask,forinstancebyshowingaprogressbarintheuserinterface.
UI线程启动AsyncTask前,会先调用AsyncTask的onPreExecute。
这个接口中的操作主要用于做一些准备工作,例如创建任务对应的进度条。
doInBackground,invokedonthebackgroundthreadimmediatelyafteronPreExecute()finishesexecuting.
Thisstepisusedtoperformbackgroundcomputationthatcantakealongtime.
Theparametersoftheasynchronoustaskarepassedtothisstep.
Theresultofthecomputationmustbereturnedbythisstepandwillbepassedbacktothelaststep.
Thisstepcanalsouse{publishProgresstopublishoneormoreunitsofprogress.
ThesevaluesarepublishedontheUIthread,intheProgressUpdatestep.
当UI线程执行完onPreExecute函数后,后台线程将立即调用doInBackground接口。
doInBackground接口负责完成主要的耗时工作,AsyncTask执行时的输入参数将传递到doInBackground接口。
doInBackground中完成耗时工作后,必须返回执行结果(与模板参数Result一致)。
在doInBackground中可以调用publishProgress接口更新进度条,
进度条的数值将通过onProgressUpdate接口更新到UI界面。
onProgressUpdate,invokedontheUIthreadafteracalltopublishProgress.Thetimingoftheexecutionisundefined.
Thismethodisusedtodisplayanyformofprogressintheuserinterfacewhilethebackgroundcomputationisstillexecuting.
Forinstance,itcanbeusedtoanimateaprogressbarorshowlogsinatextfield.
当后台线程调用了publishProgress接口后,UI线程将在合适的时间内调用onProgressUpdate函数(由系统决定调用时间)。
在onProgressUpdate函数中,UI线程可以更新进度条信息。
onPostExecute,invokedontheUIthreadafterthebackgroundcomputationfinishes.
Theresultofthebackgroundcomputationispassedtothisstepasaparameter.
后台线程完成工作后,UI线程将调用onPostExecute接口,doInBackground的返回结果将作为该函数的输入参数。
AsyncTaskmustbesubclassedtobeused.ThesubclasswilloverrideatleastonemethoddoInBackground,
andmostoftenwilloverrideasecondoneonPostExecute.
在使用AsyncTask时,通常定义继承该类的子类,并覆盖其中的doInBackground、onPostExecute等函数。
Ataskcanbecancelledatanytimebyinvokingcancel(boolean).
InvokingthismethodwillcausesubsequentcallstoisCancelled()toreturntrue.
Afterinvokingthismethod,onCancelled(Object),insteadof
onPostExecute(Object)willbeinvokedafterdoInBackground(Object[])returns.
Toensurethatataskiscancelledasquicklyaspossible,youshouldalwayscheckthereturnvalueofisCancelled()
periodicallyfromdoInBackground(Object[]),ifpossible(insidealoopforinstance.)
调用AsyncTask的cancel接口可以结束任务。
当调用了cancel接口后,doInBackground结束后,将回调onCancelled接口而不是onPostExecute接口。
为了尽可能快的相应结束命令,应该在doInBackground函数中周期性的检测isCancelled接口的返回值。
Thereareafewthreadingrulesthatmustbefollowedforthisclasstoworkproperly:
1.TheAsyncTaskclassmustbeloadedontheUIthread.
2.ThetaskinstancemustbecreatedontheUIthread.
3.executemustbeinvokedontheUIthread.
4.DonotcallonPreExecute(),onPostExecute,doInBackground,
onProgressUpdatemanually.
5.Thetaskcanbeexecutedonlyonce(anexceptionwillbethrownifasecondexecutionisattempted.)
AsyncTask的加载、创建和启动必须在UI线程完成。
不要手动调用AsyncTask的onPreExecute、onPostExecute、doInBackground和onProgressUpdate接口。
每个AsyncTask只能执行一次,重复执行将会抛出异常。
AsyncTaskguaranteesthatallcallbackcallsaresynchronizedinsuchawaythatthefollowing
operationsaresafewithoutexplicitsynchronizations.
1.SetmemberfieldsintheconstructororonPreExecute,andrefertothem
indoInBackground.
2.SetmemberfieldsindoInBackground,andrefertothemin
onProgressUpdateandonPostExecute.
为了保证线程安全,doInBackground中引用的AsyncTask成员变量,必须事先初始化于构造函数或onPreExecute。
doInBackground中初始化的成员变量,由onProgressUpdate和onPostExecute使用。
Whenfirstintroduced,AsyncTaskswereexecutedseriallyonasinglebackgroundthread.
Startingwithandroid.os.Build.VERSION_CODES.DONUT,thiswaschangedtoapoolofthreadsallowingmultipletaskstooperateinparallel.
Startingwithandroid.os.Build.VERSION_CODES.HONEYCOMB,tasksareexecutedonasinglethread
toavoidcommonapplicationerrorscausedbyparallelexecution.
Ifyoutrulywantparallelexecution,youcaninvokeexecuteOnExecutor(java.util.concurrent.Executor,Object[])withTHREAD_POOL_EXECUTOR.
从文档来看,AsyncTask默认将在单一的后台线程中执行,
如果需要并发执行,则可以使用AsyncTask的executeOnExecutor接口。
了解AsyncTask的基本定义后,使用AsyncTask就比较简单了。
2、基本用法
Android给出的使用示例如下:
//使用时继承AsyncTask,并指定模板参数
privateclassDownloadFilesTaskextendsAsyncTask{
//后台执行的任务
protectedLongdoInBackground(URL...urls){
intcount=urls.length;
longtotalSize=0;
for(inti=0;i//下载操作
totalSize+=Downloader.downloadFile(urls[i]);
//更新下载进度
publishProgress((int)((i/(float)count)*100));
//Escapeearlyifcancel()iscalled
//若主动结束AsyncTask,则结束任务
if(isCancelled())break;
}
returntotalSize;
}
protectedvoidonProgressUpdate(Integer...progress){
//UI线程更新进度
setProgressPercent(progress[0]);
}
protectedvoidonPostExecute(Longresult){
//执行完毕后,UI线程调用
showDialog("Downloaded"+result+"bytes");
}
}
AsyncTask的使用较为简单,对于上面的例子,可以按照以下方式使用:
newDownloadFilesTask().execute(url1,url2,url3);
1
1
对于APK开发者来说,掌握到这个地方就能满足开发需求了。
不过为了满足我们的好奇心,我们还是进一步看看源码是如何实现的。
3、源码分析
我们主要按照AsyncTask基本用法的流程,来分析AsyncTask的实现。
3.1构造函数
在使用AsyncTask前,会创建出AsyncTask的实例。
实际的子类会调用AsyncTask的构造函数。
AsyncTask的构造函数如下所示:
publicAsyncTask(){
//WorkerRunnable实现Callable接口
//参数类型为Params,接口函数call返回值的类型为Result
mWorker=newWorkerRunnable(){
publicResultcall()throwsException{
//mWorker在call接口中的工作先不分析,后文会提到
................
}
};
//创建一个FutureTask对象,构造函数的参数为一个callable对象
//当FutureTask执行时,会调用callable对象的call函数,在执行完毕后再回调done接口
mFuture=newFutureTask(mWorker){
@Override
protectedvoiddone(){
//此处done接口中的内容,先不分析后文会提到
................
}
};
}
从上面的代码可以看出,AsyncTask的构造函数中仅创建出了两个对象。
注意到这两个对象均创建在主线程中。
如果用命令模式来理解的话,那么AsyncTask中仅创建出了一个需要执行的命令。
这个命令还没有被添加到执行队列中。
3.2execute接口
AsyncTask有好几个execute接口,我们分析最常用的接口。
//注解说明了此接口在主线程中调用
@MainThread
publicfinalAsyncTaskexecute(Params...params){
//注意到除了传入参数外,还传入了sDefaultExecutor
returnexecuteOnExecutor(sDefaultExecutor,params);
}
在分析executeOnExecutor流程前,我们看看sDefaultExecutor。
..............
//静态成员,进程共享,默认指向SERIAL_EXECUTOR
//可以调用AsyncTask的setDefaultExecutor进行设置
//因此,设置了volatile属性,保证并发的可见性
privatestaticvolatileExecutorsDefaultExecutor=SERIAL_EXECUTOR;
............
/**
*An{@linkExecutor}thatexecutestasksoneatatimeinserial
*order.Thisserializationisglobaltoaparticularprocess.
*/
//SERIAL_EXECUTOR指向的对象为SerialExecutor
publicstaticfinalExecutorSERIAL_EXECUTOR=newSerialExecutor();
............
privatestaticclassSerialExecutorimplementsExecutor{
//队列中保存所有的任务
finalArrayDequemTasks=newArrayDeque();
//mActive中保存当前正在执行的任务
RunnablemActive;
publicsynchronizedvoidexecute(finalRunnabler){
//execute函数被调用时,将runable对象加入到mTasks中
mTasks.offer(newRunnable(){
//当该runnable被执行后,调用scheduleNext
publicvoidrun(){
try{
r.run();
}finally{
scheduleNext();
}
}
});
//当有新加入的任务时,若当前没有正在处理的任务,则直接调用scheduleNext接口
if(mActive==null){
scheduleNext();
}
}
protectedsynchronizedvoidscheduleNext(){
//从队列中取出第一个任务
if((mActive=mTasks.poll())!
=null){
//利用THREAD_POOL_EXECUTOR执行
//根据execute中的定义,我们知道一个runable的run方法结束后
//才会重新调用scheduleNext
//因此,虽然THREAD_POOL_EXECUTOR可以同时创建多个线程
//但AsyncTask还是一个接一个的处理任务
//如果将本函数的if改成while,就是并发处理了
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
THREAD_POOL_EXECUTOR为AsyncTask加载时创建出的ThreadPoolExecutor,
由于SerialExecutor限制任务必须单一依次执行,因此我们不再关注THREAD_POOL_EXECUTOR的参数。
3.3executeOnExecutor接口
现在我们将视线拉回到executeOnExecutor接口。
@MainThread
publicfinalAsyncTaskexecuteOnExecutor(Executorexec,
Params...params){
//AsyncTask中定义了:
privatevolatileStatusmStatus=Status.PENDING;
//因此每个AsyncTask对象创建后,其状态都是PENDING
//这里就是文档中叙述的,每个AsyncTask只能运行一次的原因
if(mStatus!
=Status.PENDING){
switch(mStatus){
caseRUNNING:
thrownewIllegalStateException("Cannotexecutetask:
"
+"theskisalreadyrunning.");
caseFINISHED:
thrownewIllegalStateException("Cannotexecutetask:
"
+"thetaskhasalreadybeenexecuted"
+"(ataskcanbeexecutedonlyonce)");
}
}
//AsyncTask开始运行后,