Android AsyncTask 深入解析
1、概述
相信大家对AsyncTask都不陌生,对于执行耗时任务,然后更新UI是一把利器,当然也是替代Thread + Handler 的一种方式。如果你对Handler机制还不了解,请看:Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系。
2、简单的例子
相信大家都写过这样的代码:
[java] view plain copy- packagecom.example.zhy_asynctask_demo01;
- importandroid.app.Activity;
- importandroid.app.ProgressDialog;
- importandroid.os.AsyncTask;
- importandroid.os.Bundle;
- importandroid.util.Log;
- importandroid.widget.TextView;
- publicclassMainActivityextendsActivity
- {
- privatestaticfinalStringTAG="MainActivity";
- privateProgressDialogmDialog;
- privateTextViewmTextView;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mTextView=(TextView)findViewById(R.id.id_tv);
- mDialog=newProgressDialog(this);
- mDialog.setMax(100);
- mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mDialog.setCancelable(false);
- newMyAsyncTask().execute();
- }
- privateclassMyAsyncTaskextendsAsyncTask<Void,Integer,Void>
- {
- @Override
- protectedvoidonPreExecute()
- {
- mDialog.show();
- Log.e(TAG,Thread.currentThread().getName()+"onPreExecute");
- }
- @Override
- protectedVoiddoInBackground(Void...params)
- {
- //模拟数据的加载,耗时的任务
- for(inti=0;i<100;i++)
- {
- try
- {
- Thread.sleep(80);
- }catch(InterruptedExceptione)
- {
- e.printStackTrace();
- }
- publishProgress(i);
- }
- Log.e(TAG,Thread.currentThread().getName()+"doInBackground");
- returnnull;
- }
- @Override
- protectedvoidonProgressUpdate(Integer...values)
- {
- mDialog.setProgress(values[0]);
- Log.e(TAG,Thread.currentThread().getName()+"onProgressUpdate");
- }
- @Override
- protectedvoidonPostExecute(Voidresult)
- {
- //进行数据加载完成后的UI操作
- mDialog.dismiss();
- mTextView.setText("LOADDATASUCCESS");
- Log.e(TAG,Thread.currentThread().getName()+"onPostExecute");
- }
- }
- }
进入某个Activity,Activity中需要的数据来自于网络或者其它耗时操作,可以在AsyncTask中onPreExecute完成一些准备操作,比如上例中显示进度对话框;然后在doInBackground完成耗时操作,在进行耗时操作时还能不时的通过publishProgress给onProgressUpdate中传递参数,然后在onProgressUpdate中可以进行UI操作,比如上例更新进度条的进度;当耗时任务执行完成后,最后在onPostExecute进行设置控件数据更新UI等操作,例如隐藏进度对话框。
效果图:
3、源码解析
注:本篇源码分析基于Andorid-17,因为和3.0之前版本变动较大,有必要标出。那么大家一定好奇,AsyncTask在Android中是如何实现的,下面进行源码分析:从我们的执行异步任务的起点开始,进入execute方法:
- publicfinalAsyncTask<Params,Progress,Result>execute(Params...params){
- returnexecuteOnExecutor(sDefaultExecutor,params);
- }
- publicfinalAsyncTask<Params,Progress,Result>executeOnExecutor(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;
- }
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams
23行:exec.execute(mFuture)
相信大家对22行出现的mWorker,以及23行出现的mFuture都会有些困惑。
mWorker找到这个类:
- privatestaticabstractclassWorkerRunnable<Params,Result>implementsCallable<Result>{
- Params[]mParams;
- }
可以看到是Callable的子类,且包含一个mParams用于保存我们传入的参数,下面看初始化mWorker的代码:
[java] view plain copy
- publicAsyncTask(){
- mWorker=newWorkerRunnable<Params,Result>(){
- publicResultcall()throwsException{
- mTaskInvoked.set(true);
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- //noinspectionunchecked
- returnpostResult(doInBackground(mParams));
- }
- };
- //….
- }
可以看到mWorker在构造方法中完成了初始化,并且因为是一个抽象类,在这里new了一个实现类,实现了call方法,call方法中设置mTaskInvoked=true,且最终调用doInBackground(mParams)方法,并返回Result值作为参数给postResult方法.可以看到我们的doInBackground出现了,下面继续看:
[java] view plain copy
- privateResultpostResult(Resultresult){
- @SuppressWarnings("unchecked")
- Messagemessage=sHandler.obtainMessage(MESSAGE_POST_RESULT,
- newAsyncTaskResult<Result>(this,result));
- message.sendToTarget();
- returnresult;
- }
可以看到postResult中出现了我们熟悉的异步消息机制,传递了一个消息message, message.what为MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);
[java] view plain copy
- privatestaticclassAsyncTaskResult<Data>{
- finalAsyncTaskmTask;
- finalData[]mData;
- AsyncTaskResult(AsyncTasktask,Data...data){
- mTask=task;
- mData=data;
- }
- }
AsyncTaskResult就是一个简单的携带参数的对象。
看到这,我相信大家肯定会想到,在某处肯定存在一个sHandler,且复写了其handleMessage方法等待消息的传入,以及消息的处理。
- privatestaticfinalInternalHandlersHandler=newInternalHandler();
- privatestaticclassInternalHandlerextendsHandler{
- @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;
- }
- }
- }
哈哈,出现了我们的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result),于是看finish方法 [java] view plain copy
- privatevoidfinish(Resultresult){
- if(isCancelled()){
- onCancelled(result);
- }else{
- onPostExecute(result);
- }
- mStatus=Status.FINISHED;
- }
可以看到,如果我们调用了cancel()则执行onCancelled回调;正常执行的情况下调用我们的onPostExecute(result);主要这里的调用是在handler的handleMessage中,所以是在UI线程中。如果你对异步消息机制不理解请看: Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
最后将状态置为FINISHED。
mWoker看完了,应该到我们的mFuture了,依然实在构造方法中完成mFuture的初始化,将mWorker作为参数,复写了其done方法。
- publicAsyncTask(){
- ...
- mFuture=newFutureTask<Result>(mWorker){
- @Override
- protectedvoiddone(){
- try{
- postResultIfNotInvoked(get());
- }catch(InterruptedExceptione){
- android.util.Log.w(LOG_TAG,e);
- }catch(ExecutionExceptione){
- thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()",
- e.getCause());
- }catch(CancellationExceptione){
- postResultIfNotInvoked(null);
- }
- }
- };
- }
- privatevoidpostResultIfNotInvoked(Resultresult){
- finalbooleanwasTaskInvoked=mTaskInvoked.get();
- if(!wasTaskInvoked){
- postResult(result);
- }
- }
好了,到了这里,已经介绍完了execute方法中出现了mWorker和mFurture,不过这里一直是初始化这两个对象的代码,并没有真正的执行。下面我们看真正调用执行的地方。
execute方法中的:
还记得上面的execute中的23行:exec.execute(mFuture)
exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor
[java] view plain copy
- privatestaticvolatileExecutorsDefaultExecutor=SERIAL_EXECUTOR;
- publicstaticfinalExecutorSERIAL_EXECUTOR=newSerialExecutor();
- privatestaticclassSerialExecutorimplementsExecutor{
- finalArrayDeque<Runnable>mTasks=newArrayDeque<Runnable>();
- 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);
- }
- }
- }
16-17行:判断当前mActive是否为空,为空则调用scheduleNext方法
20行:scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
下面看THREAD_POOL_EXECUTOR为何方神圣:
[java] view plain copy
- publicstaticfinalExecutorTHREAD_POOL_EXECUTOR
- =newThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
- TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
- privatestaticfinalintCORE_POOL_SIZE=5;
- privatestaticfinalintMAXIMUM_POOL_SIZE=128;
- privatestaticfinalintKEEP_ALIVE=1;
- privatestaticfinalThreadFactorysThreadFactory=newThreadFactory(){
- privatefinalAtomicIntegermCount=newAtomicInteger(1);
- publicThreadnewThread(Runnabler){
- returnnewThread(r,"AsyncTask#"+mCount.getAndIncrement());
- }
- };
- privatestaticfinalBlockingQueue<Runnable>sPoolWorkQueue=
- newLinkedBlockingQueue<Runnable>(10);
看到这里,大家可能会认为,背后原来有一个线程池,且最大支持128的线程并发,加上长度为10的阻塞队列,可能会觉得就是在快速调用138个以内的AsyncTask子类的execute方法不会出现问题,而大于138则会抛出异常。
其实不是这样的,我们再仔细看一下代码,回顾一下sDefaultExecutor,真正在execute()中调用的为sDefaultExecutor.execute:
[java] view plain copy
- privatestaticclassSerialExecutorimplementsExecutor{
- finalArrayDeque<Runnable>mTasks=newArrayDeque<Runnable>();
- 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);
- }
- }
- }
可以看到,如果此时有10个任务同时调用execute(s synchronized)方法,第一个任务入队,然后在mActive = mTasks.poll()) != null被取出,并且赋值给mActivte,然后交给线程池去执行。然后第二个任务入队,但是此时mActive并不为null,并不会执行scheduleNext();所以如果第一个任务比较慢,10个任务都会进入队列等待;真正执行下一个任务的时机是,线程池执行完成第一个任务以后,调用Runnable中的finally代码块中的scheduleNext,所以虽然内部有一个线程池,其实调用的过程还是线性的。一个接着一个的执行,相当于单线程。
4、总结
到此源码解释完毕,由于代码跨度比较大,我们再回顾一下:
[java] view plain copy- publicfinalAsyncTask<Params,Progress,Result>execute(Params...params){
- returnexecuteOnExecutor(sDefaultExecutor,params);
- }
- publicfinalAsyncTask<Params,Progress,Result>executeOnExecutor(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;
- }
18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams ,mWorker为一个Callable的子类,且在内部的call()方法中,调用了doInBackground(mParams),然后得到的返回值作为postResult的参数进行执行;postResult中通过sHandler发送消息,最终sHandler的handleMessage中完成onPostExecute的调用。
23行:exec.execute(mFuture),mFuture为真正的执行任务的单元,将mWorker进行封装,然后由sDefaultExecutor交给线程池进行执行。
5、publishProgress
说了这么多,我们好像忘了一个方法:publishProgress
[java] view plain copy- protectedfinalvoidpublishProgress(Progress...values){
- if(!isCancelled()){
- sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
- newAsyncTaskResult<Progress>(this,values)).sendToTarget();
- }
- }
- privatestaticclassInternalHandlerextendsHandler{
- @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;
- }
- }
- }
在handleMessage中进行了我们的onProgressUpdate(result.mData);的调用。
6、AsyncTask曾经缺陷
记得以前有个面试题经常会问道:AsyncTask运行的原理是什么?有什么缺陷?
以前对于缺陷的答案可能是:AsyncTask在并发执行多个任务时发生异常。其实还是存在的,在3.0以前的系统中还是会以支持多线程并发的方式执行,支持并发数也是我们上面所计算的128,阻塞队列可以存放10个;也就是同时执行138个任务是没有问题的;而超过138会马上出现java.util.concurrent.RejectedExecutionException;
而在在3.0以上包括3.0的系统中会为单线程执行(即我们上面代码的分析);
空说无凭:下面看测试代码:
[java] view plain copy- packagecom.example.zhy_asynctask_demo01;
- importandroid.app.Activity;
- importandroid.app.ProgressDialog;
- importandroid.os.AsyncTask;
- importandroid.os.Bundle;
- importandroid.util.Log;
- importandroid.widget.TextView;
- publicclassMainActivityextendsActivity
- {
- privatestaticfinalStringTAG="MainActivity";
- privateProgressDialogmDialog;
- privateTextViewmTextView;
- @Override
- protectedvoidonCreate(BundlesavedInstanceState)
- {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mTextView=(TextView)findViewById(R.id.id_tv);
- mDialog=newProgressDialog(this);
- mDialog.setMax(100);
- mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
- mDialog.setCancelable(false);
- for(inti=1;i<=138;i++)
- {
- newMyAsyncTask2().execute();
- }
- //newMyAsyncTask().execute();
- }
- privateclassMyAsyncTask2extendsAsyncTask<Void,Void,Void>
- {
- @Override
- protectedVoiddoInBackground(Void...params)
- {
- try
- {
- Log.e(TAG,Thread.currentThread().getName());
- Thread.sleep(10000);
- }catch(InterruptedExceptione)
- {
- e.printStackTrace();
- }
- returnnull;
- }
- }
- }
输出结果为:
AsyncTask#1 - AsyncTask #128同时输出
然后10s后,另外10个任务输出。
可以分析结果,得到结论:AsyncTask在2.2的系统中同时支持128个任务并发,至少支持10个任务等待;
下面将138个任务,改成139个任务:
[java] view plain copy- for(inti=1;i<=139;i++)
- {
- newMyAsyncTask2().execute();
- }
简单说一下出现异常的原因:现在是139个任务,几乎同时提交,线程池支持128个的并发,然后阻塞队列数量为10,此时当第11个任务提交的时候则会发生异常。
简单看一下源码:
[java] view plain copy- publicstaticfinalExecutorTHREAD_POOL_EXECUTOR
- =newThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
- 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);
当阻塞队列满的时候workQueue.offer(command)返回false;然后执行addWorker(command,false)方法,如果返回false则执行reject()方法. [java] view plain copy
- privatebooleanaddWorker(RunnablefirstTask,booleancore){
- …
- intwc=workerCountOf(c);
- if(wc>=CAPACITY||
- wc>=(core?corePoolSize:maximumPoolSize))
- returnfalse;
- …
- }
可以看到当任务数目大于容量则返回false,最终在reject()中抛出异常。
上面就是使用2.2模拟器测试的结果;
下面将系统改为4.1.1,也就是我的测试机小米2s
把线程数改为139甚至1000,你可以看到任务一个接一个的在那缓慢的执行,不会抛什么异常,不过线程倒是1个1个的在那执行;
好了,如果现在大家去面试,被问到AsyncTask的缺陷,可以分为两个部分说,在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行;
至此,AsyncTask源码分析完毕,相信大家对AsyncTask有了更深的理解~~~
测试代码点击下载
更多相关文章
- Android 2.3.3 安卓系统 源代码 在Eclipse查看方法
- mtk android内置nginx服务器的方法
- ubuntu 编译 Android 出现的若干错误及解决方法
- Android保持屏幕常亮的三种方法