文章转载自http://blog.csdn.net/liuhe688/article/details/6532519


在Android中实现异步任务机制有两种方式,Handler和AsyncTask。

Handler模式需要为每一个任务创建一个新的线程,任务完成后通过Handler实例向UI线程发送消息,完成界面的更新,这种方式对于整个过程的控制比较精细,但也是有缺点的,例如代码相对臃肿,在多个任务同时执行时,不易对线程进行精确的控制。关于Handler的相关知识,前面也有所介绍,不清楚的朋友们可以参照一下。

为了简化操作,Android1.5提供了工具类android.os.AsyncTask,它使创建异步任务变得更加简单,不再需要编写任务线程和Handler实例即可完成相同的任务。

先来看看AsyncTask的定义:

[java] view plain copy
  1. publicabstractclassAsyncTask<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执行异步任务操作,我们先建立一个项目,结构如下:

结构相对简单一些,让我们先看看MainActivity.java的代码:

[java] view plain copy
  1. packagecom.scott.async;
  2. importjava.io.ByteArrayOutputStream;
  3. importjava.io.InputStream;
  4. importorg.apache.http.HttpEntity;
  5. importorg.apache.http.HttpResponse;
  6. importorg.apache.http.HttpStatus;
  7. importorg.apache.http.client.HttpClient;
  8. importorg.apache.http.client.methods.HttpGet;
  9. importorg.apache.http.impl.client.DefaultHttpClient;
  10. importandroid.app.Activity;
  11. importandroid.os.AsyncTask;
  12. importandroid.os.Bundle;
  13. importandroid.util.Log;
  14. importandroid.view.View;
  15. importandroid.widget.Button;
  16. importandroid.widget.ProgressBar;
  17. importandroid.widget.TextView;
  18. publicclassMainActivityextendsActivity{
  19. privatestaticfinalStringTAG="ASYNC_TASK";
  20. privateButtonexecute;
  21. privateButtoncancel;
  22. privateProgressBarprogressBar;
  23. privateTextViewtextView;
  24. privateMyTaskmTask;
  25. @Override
  26. publicvoidonCreate(BundlesavedInstanceState){
  27. super.onCreate(savedInstanceState);
  28. setContentView(R.layout.main);
  29. execute=(Button)findViewById(R.id.execute);
  30. execute.setOnClickListener(newView.OnClickListener(){
  31. @Override
  32. publicvoidonClick(Viewv){
  33. //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
  34. mTask=newMyTask();
  35. mTask.execute("http://www.baidu.com");
  36. execute.setEnabled(false);
  37. cancel.setEnabled(true);
  38. }
  39. });
  40. cancel=(Button)findViewById(R.id.cancel);
  41. cancel.setOnClickListener(newView.OnClickListener(){
  42. @Override
  43. publicvoidonClick(Viewv){
  44. //取消一个正在执行的任务,onCancelled方法将会被调用
  45. mTask.cancel(true);
  46. }
  47. });
  48. progressBar=(ProgressBar)findViewById(R.id.progress_bar);
  49. textView=(TextView)findViewById(R.id.text_view);
  50. }
  51. privateclassMyTaskextendsAsyncTask<String,Integer,String>{
  52. //onPreExecute方法用于在执行后台任务前做一些UI操作
  53. @Override
  54. protectedvoidonPreExecute(){
  55. Log.i(TAG,"onPreExecute()called");
  56. textView.setText("loading...");
  57. }
  58. //doInBackground方法内部执行后台任务,不可在此方法内修改UI
  59. @Override
  60. protectedStringdoInBackground(String...params){
  61. Log.i(TAG,"doInBackground(Params...params)called");
  62. try{
  63. HttpClientclient=newDefaultHttpClient();
  64. HttpGetget=newHttpGet(params[0]);
  65. HttpResponseresponse=client.execute(get);
  66. if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){
  67. HttpEntityentity=response.getEntity();
  68. InputStreamis=entity.getContent();
  69. longtotal=entity.getContentLength();
  70. ByteArrayOutputStreambaos=newByteArrayOutputStream();
  71. byte[]buf=newbyte[1024];
  72. intcount=0;
  73. intlength=-1;
  74. while((length=is.read(buf))!=-1){
  75. baos.write(buf,0,length);
  76. count+=length;
  77. //调用publishProgress公布进度,最后onProgressUpdate方法将被执行
  78. publishProgress((int)((count/(float)total)*100));
  79. //为了演示进度,休眠500毫秒
  80. Thread.sleep(500);
  81. }
  82. returnnewString(baos.toByteArray(),"gb2312");
  83. }
  84. }catch(Exceptione){
  85. Log.e(TAG,e.getMessage());
  86. }
  87. returnnull;
  88. }
  89. //onProgressUpdate方法用于更新进度信息
  90. @Override
  91. protectedvoidonProgressUpdate(Integer...progresses){
  92. Log.i(TAG,"onProgressUpdate(Progress...progresses)called");
  93. progressBar.setProgress(progresses[0]);
  94. textView.setText("loading..."+progresses[0]+"%");
  95. }
  96. //onPostExecute方法用于在执行完后台任务后更新UI,显示结果
  97. @Override
  98. protectedvoidonPostExecute(Stringresult){
  99. Log.i(TAG,"onPostExecute(Resultresult)called");
  100. textView.setText(result);
  101. execute.setEnabled(true);
  102. cancel.setEnabled(false);
  103. }
  104. //onCancelled方法用于在取消执行中的任务时更改UI
  105. @Override
  106. protectedvoidonCancelled(){
  107. Log.i(TAG,"onCancelled()called");
  108. textView.setText("cancelled");
  109. progressBar.setProgress(0);
  110. execute.setEnabled(true);
  111. cancel.setEnabled(false);
  112. }
  113. }
  114. }

布局文件main.xml代码如下:

[xhtml] view plain copy
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent">
  6. <Button
  7. android:id="@+id/execute"
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:text="execute"/>
  11. <Button
  12. android:id="@+id/cancel"
  13. android:layout_width="fill_parent"
  14. android:layout_height="wrap_content"
  15. android:enabled="false"
  16. android:text="cancel"/>
  17. <ProgressBar
  18. android:id="@+id/progress_bar"
  19. android:layout_width="fill_parent"
  20. android:layout_height="wrap_content"
  21. android:progress="0"
  22. android:max="100"
  23. style="?android:attr/progressBarStyleHorizontal"/>
  24. <ScrollView
  25. android:layout_width="fill_parent"
  26. android:layout_height="wrap_content">
  27. <TextView
  28. android:id="@+id/text_view"
  29. android:layout_width="fill_parent"
  30. android:layout_height="wrap_content"/>
  31. </ScrollView>
  32. </LinearLayout>

因为需要访问网络,所以我们还需要在AndroidManifest.xml中加入访问网络的权限:

[xhtml] view plain copy
  1. <uses-permissionandroid:name="android.permission.INTERNET"/>

我们来看一下运行时的界面:

以上几个截图分别是初始界面、执行异步任务时界面、执行成功后界面、取消任务后界面。执行成功后,整个过程日志打印如下:

如果我们在执行任务时按下了“cancel”按钮,日志打印如下:

可以看到onCancelled()方法将会被调用,onPostExecute(Result result)方法将不再被调用。

上面介绍了AsyncTask的基本应用,有些朋友也许会有疑惑,AsyncTask内部是怎么执行的呢,它执行的过程跟我们使用Handler又有什么区别呢?答案是:AsyncTask是对Thread+Handler良好的封装,在android.os.AsyncTask代码里仍然可以看到Thread和Handler的踪迹。下面就向大家详细介绍一下AsyncTask的执行原理。

我们先看一下AsyncTask的大纲视图:

我们可以看到关键几个步骤的方法都在其中,doInBackground(Params... params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;onPreExecute()、onProgressUpdate(Progress... values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;publishProgress(Progress... values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params... params)中调用此方法;另外,我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类代码段如下:

[java] view plain copy
  1. //初始状态
  2. privatevolatileStatusmStatus=Status.PENDING;
  3. publicenumStatus{
  4. /**
  5. *Indicatesthatthetaskhasnotbeenexecutedyet.
  6. */
  7. PENDING,
  8. /**
  9. *Indicatesthatthetaskisrunning.
  10. */
  11. RUNNING,
  12. /**
  13. *Indicatesthat{@linkAsyncTask#onPostExecute}hasfinished.
  14. */
  15. FINISHED,
  16. }
  17. /**
  18. *Returnsthecurrentstatusofthistask.
  19. *
  20. *@returnThecurrentstatus.
  21. */
  22. publicfinalStatusgetStatus(){
  23. returnmStatus;
  24. }

可以看到,AsyncTask的初始状态为PENDING,代表待定状态,RUNNING代表执行状态,FINISHED代表结束状态,这几种状态在AsyncTask一次生命周期内的很多地方被使用,非常重要。

介绍完大纲视图相关内容之后,接下来,我们会从execute(Params... params)作为入口,重点分析一下AsyncTask的执行流程,我们来看一下execute(Params... params)方法的代码段:

[java] view plain copy
  1. publicfinalAsyncTask<Params,Progress,Result>execute(Params...params){
  2. if(mStatus!=Status.PENDING){
  3. switch(mStatus){
  4. caseRUNNING:
  5. //如果该任务正在被执行则抛出异常
  6. //值得一提的是,在调用cancel取消任务后,状态仍未RUNNING
  7. thrownewIllegalStateException("Cannotexecutetask:"
  8. +"thetaskisalreadyrunning.");
  9. caseFINISHED:
  10. //如果该任务已经执行完成则抛出异常
  11. thrownewIllegalStateException("Cannotexecutetask:"
  12. +"thetaskhasalreadybeenexecuted"
  13. +"(ataskcanbeexecutedonlyonce)");
  14. }
  15. }
  16. //改变状态为RUNNING
  17. mStatus=Status.RUNNING;
  18. //调用onPreExecute方法
  19. onPreExecute();
  20. mWorker.mParams=params;
  21. sExecutor.execute(mFuture);
  22. returnthis;
  23. }

代码中涉及到三个陌生的变量:mWorker、sExecutor、mFuture,我们也会看一下他们的庐山真面目:

关于sExecutor,它是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行。代码如下:

[java] view plain copy
  1. privatestaticfinalintCORE_POOL_SIZE=5;
  2. privatestaticfinalintMAXIMUM_POOL_SIZE=128;
  3. privatestaticfinalintKEEP_ALIVE=10;
  4. //新建一个队列用来存放线程
  5. privatestaticfinalBlockingQueue<Runnable>sWorkQueue=
  6. newLinkedBlockingQueue<Runnable>(10);
  7. //新建一个线程工厂
  8. privatestaticfinalThreadFactorysThreadFactory=newThreadFactory(){
  9. privatefinalAtomicIntegermCount=newAtomicInteger(1);
  10. //新建一个线程
  11. publicThreadnewThread(Runnabler){
  12. returnnewThread(r,"AsyncTask#"+mCount.getAndIncrement());
  13. }
  14. };
  15. //新建一个线程池执行器,用于管理线程的执行
  16. privatestaticfinalThreadPoolExecutorsExecutor=newThreadPoolExecutor(CORE_POOL_SIZE,
  17. MAXIMUM_POOL_SIZE,KEEP_ALIVE,TimeUnit.SECONDS,sWorkQueue,sThreadFactory);

mWorker实际上是AsyncTask的一个的抽象内部类的实现对象实例,它实现了Callable<Result>接口中的call()方法,代码如下:

[java] view plain copy
  1. privatestaticabstractclassWorkerRunnable<Params,Result>implementsCallable<Result>{
  2. Params[]mParams;
  3. }

而mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息:

[java] view plain copy
  1. /**
  2. *Acancellableasynchronouscomputation.
  3. *...
  4. */
  5. publicclassFutureTask<V>implementsRunnableFuture<V>{

[java] view plain copy
  1. publicinterfaceRunnableFuture<V>extendsRunnable,Future<V>{
  2. /**
  3. *SetsthisFuturetotheresultofitscomputation
  4. *unlessithasbeencancelled.
  5. */
  6. voidrun();
  7. }

可以看到FutureTask是一个可以中途取消的用于异步计算的类。

下面是mWorker和mFuture实例在AsyncTask中的体现:

[java] view plain copy
  1. privatefinalWorkerRunnable<Params,Result>mWorker;
  2. privatefinalFutureTask<Result>mFuture;
  3. publicAsyncTask(){
  4. mWorker=newWorkerRunnable<Params,Result>(){
  5. //call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法
  6. publicResultcall()throwsException{
  7. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  8. returndoInBackground(mParams);
  9. }
  10. };
  11. //在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法
  12. mFuture=newFutureTask<Result>(mWorker){
  13. @Override
  14. protectedvoiddone(){
  15. Messagemessage;
  16. Resultresult=null;
  17. try{
  18. result=get();
  19. }catch(InterruptedExceptione){
  20. android.util.Log.w(LOG_TAG,e);
  21. }catch(ExecutionExceptione){
  22. thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()",
  23. e.getCause());
  24. }catch(CancellationExceptione){
  25. //发送取消任务的消息
  26. message=sHandler.obtainMessage(MESSAGE_POST_CANCEL,
  27. newAsyncTaskResult<Result>(AsyncTask.this,(Result[])null));
  28. message.sendToTarget();
  29. return;
  30. }catch(Throwablet){
  31. thrownewRuntimeException("Anerroroccuredwhileexecuting"
  32. +"doInBackground()",t);
  33. }
  34. //发送显示结果的消息
  35. message=sHandler.obtainMessage(MESSAGE_POST_RESULT,
  36. newAsyncTaskResult<Result>(AsyncTask.this,result));
  37. message.sendToTarget();
  38. }
  39. };
  40. }

我们看到上面的代码中,mFuture实例对象的done()方法中,如果捕捉到了CancellationException类型的异常,则发送一条“MESSAGE_POST_CANCEL”的消息;如果顺利执行,则发送一条“MESSAGE_POST_RESULT”的消息,而消息都与一个sHandler对象关联。这个sHandler实例实际上是AsyncTask内部类InternalHandler的实例,而InternalHandler正是继承了Handler,下面我们来分析一下它的代码:

[java] view plain copy
  1. privatestaticfinalintMESSAGE_POST_RESULT=0x1;//显示结果
  2. privatestaticfinalintMESSAGE_POST_PROGRESS=0x2;//更新进度
  3. privatestaticfinalintMESSAGE_POST_CANCEL=0x3;//取消任务
  4. privatestaticfinalInternalHandlersHandler=newInternalHandler();
  5. privatestaticclassInternalHandlerextendsHandler{
  6. @SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
  7. @Override
  8. publicvoidhandleMessage(Messagemsg){
  9. AsyncTaskResultresult=(AsyncTaskResult)msg.obj;
  10. switch(msg.what){
  11. caseMESSAGE_POST_RESULT:
  12. //Thereisonlyoneresult
  13. //调用AsyncTask.finish方法
  14. result.mTask.finish(result.mData[0]);
  15. break;
  16. caseMESSAGE_POST_PROGRESS:
  17. //调用AsyncTask.onProgressUpdate方法
  18. result.mTask.onProgressUpdate(result.mData);
  19. break;
  20. caseMESSAGE_POST_CANCEL:
  21. //调用AsyncTask.onCancelled方法
  22. result.mTask.onCancelled();
  23. break;
  24. }
  25. }
  26. }

我们看到,在处理消息时,遇到“MESSAGE_POST_RESULT”时,它会调用AsyncTask中的finish()方法,我们来看一下finish()方法的定义:

[java] view plain copy
  1. privatevoidfinish(Resultresult){
  2. if(isCancelled())result=null;
  3. onPostExecute(result);//调用onPostExecute显示结果
  4. mStatus=Status.FINISHED;//改变状态为FINISHED
  5. }

原来finish()方法是负责调用onPostExecute(Result result)方法显示结果并改变任务状态的啊。

另外,在mFuture对象的done()方法里,构建一个消息时,这个消息包含了一个AsyncTaskResult类型的对象,然后在sHandler实例对象的handleMessage(Message msg)方法里,使用下面这种方式取得消息中附带的对象:

[java] view plain copy
  1. AsyncTaskResultresult=(AsyncTaskResult)msg.obj;

这个AsyncTaskResult究竟是什么呢,它又包含什么内容呢?其实它也是AsyncTask的一个内部类,是用来包装执行结果的一个类,让我们来看一下它的代码结构:

[java] view plain copy
  1. @SuppressWarnings({"RawUseOfParameterizedType"})
  2. privatestaticclassAsyncTaskResult<Data>{
  3. finalAsyncTaskmTask;
  4. finalData[]mData;
  5. AsyncTaskResult(AsyncTasktask,Data...data){
  6. mTask=task;
  7. mData=data;
  8. }
  9. }

看以看到这个AsyncTaskResult封装了一个AsyncTask的实例和某种类型的数据集,我们再来看一下构建消息时的代码:

[java] view plain copy
  1. //发送取消任务的消息
  2. message=sHandler.obtainMessage(MESSAGE_POST_CANCEL,
  3. newAsyncTaskResult<Result>(AsyncTask.this,(Result[])null));
  4. message.sendToTarget();

[java] view plain copy
  1. //发送显示结果的消息
  2. message=sHandler.obtainMessage(MESSAGE_POST_RESULT,
  3. newAsyncTaskResult<Result>(AsyncTask.this,result));
  4. message.sendToTarget();

在处理消息时是如何使用这个对象呢,我们再来看一下:

[java] view plain copy
  1. result.mTask.finish(result.mData[0]);

[java] view plain copy
  1. result.mTask.onProgressUpdate(result.mData);

概括来说,当我们调用execute(Params... params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个FutureTask任务,这个过程中doInBackground(Params... params)将被调用,如果被开发者覆写的doInBackground(Params... params)方法中调用了publishProgress(Progress... values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress... values)方法将被调用;如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。

经过上面的介绍,相信朋友们都已经认识到AsyncTask的本质了,它对Thread+Handler的良好封装,减少了开发者处理问题的复杂度,提高了开发效率,希望朋友们能多多体会一下。


更多相关文章

  1. Android(安卓)Notification 详解
  2. Android(安卓)ViewPager图片滑动
  3. 配置android app 方法数超过65k问题
  4. 关于Android(安卓)Studio打包混淆以及上传mapping文件
  5. 获取Android(安卓)SDK 源代码并在Eclipse中关联查看的方法
  6. websocket实现android消息推送
  7. Android(安卓)屏幕旋转时Activity的变化
  8. Android(安卓)DownLoadManager的使用
  9. 对android中MIME类型的理解

随机推荐

  1. 电商思路用于恶意软件售卖?用服务口碑抢占
  2. Oracle ADW 机器学习自动化加速实现客户
  3. VMware vSphere虚拟化-VMware ESXi 5.5组
  4. Centos下SVN环境部署记录
  5. DB_NAME、DB_UNIQUE_NAME、SERVICE_NAME
  6. ASP.NET Core WebApi基于JWT实现接口授权
  7. CSS实现水平垂直居中
  8. Linux TOP命令
  9. unknown protocol: hdfs
  10. 80 多个 Linux 系统管理员必备的监控工具