android AsyncTask介绍



AsyncTask和Handler对比

1 ) AsyncTask实现的原理,和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:

l  简单,快捷

l  过程可控

       

使用的缺点:

l  在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

2 )Handler异步实现的原理和适用的优缺点

在Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程)àthread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:

l  结构清晰,功能定义明确

l  对于多个后台任务时,简单,清晰

   

使用的缺点:

l  在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)



AsyncTask介绍



Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。



首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。



 



Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。

AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。

 

AsyncTask定义了三种泛型类型 Params,Progress和Result。



  • Params 启动任务执行的输入参数,比如HTTP请求的URL。
  • Progress 后台任务执行的百分比。
  • Result 后台执行任务最终返回的结果,比如String。



使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:

  • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。
  • onPostExecute(Result)  相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

有必要的话你还得重写以下这三个方法,但不是必须的:

  • onProgressUpdate(Progress…)   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。
  • onPreExecute()        这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
  • onCancelled()             用户调用取消时,要做的操作

使用AsyncTask类,以下是几条必须遵守的准则:

  • Task的实例必须在UI thread中创建;
  • execute方法必须在UI thread中调用;
  • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
  • 该task只能被执行一次,否则多次调用时将会出现异常;

一个超简单的理解 AsyncTask 的例子:

main.xml

1. <?xml version="1.0" encoding="utf-8"?>  
2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
3. "vertical"  
4. "fill_parent"  
5. "fill_parent"  
6.     >  
7.     <TextView    
8. "@+id/textView01"  
9. "fill_parent"   
10. "wrap_content"   
11.     />  
12.    <ProgressBar   
13. "@+id/progressBar02"  
14. "fill_parent"   
15. "wrap_content"   
16. "?android:attr/progressBarStyleHorizontal"   
17.     />  
18.     <Button  
19. "@+id/button03"  
20. "fill_parent"   
21. "wrap_content"   
22. "更新progressbar"  
23.     />  
24. </LinearLayout>


MainActivity.java


1. package vic.wong.main;  
2.   
3. import android.app.Activity;  
4. import android.os.Bundle;  
5. import android.view.View;  
6. import android.view.View.OnClickListener;  
7. import android.widget.Button;  
8. import android.widget.ProgressBar;  
9. import android.widget.TextView;  
10.   
11. public class MainActivity extends Activity {  
12.     private Button button;  
13.     private ProgressBar progressBar;  
14.     private TextView textView;  
15.       
16.     @Override  
17.     public void onCreate(Bundle savedInstanceState) {  
18.         super.onCreate(savedInstanceState);  
19.         setContentView(R.layout.main);  
20.           
21. button = (Button)findViewById(R.id.button03);  
22. progressBar = (ProgressBar)findViewById(R.id.progressBar02);  
23. textView = (TextView)findViewById(R.id.textView01);  
24.           
25.         button.setOnClickListener(new OnClickListener() {  
26.               
27.             @Override  
28.             public void onClick(View v) {  
29. asyncTask = new ProgressBarAsyncTask(textView, progressBar);  
30.                 asyncTask.execute(1000);  
31.             }  
32.         });  
33.     }  
34. }

 

NetOperator.java


1. package vic.wong.main;  
2.   
3.   
4. //模拟网络环境  
5. public class NetOperator {  
6.       
7.     public void operator(){  
8.         try {  
9.             //休眠1秒  
10.             Thread.sleep(1000);  
11.         } catch (InterruptedException e) {  
12.             // TODO Auto-generated catch block  
13.             e.printStackTrace();  
14.         }  
15.     }  
16.   
17. }


 

ProgressBarAsyncTask .java 

1. package vic.wong.main;  
2. import android.os.AsyncTask;  
3. import android.widget.ProgressBar;  
4. import android.widget.TextView;  
5.   
6. /**  
7.  * 生成该类的对象,并调用execute方法之后  
8.  * 首先执行的是onProExecute方法  
9.  * 其次执行doInBackgroup方法  
10.  *  
11.  */  
12. public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {  
13.   
14.     private TextView textView;  
15.     private ProgressBar progressBar;  
16.       
17.       
18.     public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {  
19.         super();  
20. this.textView = textView;  
21. this.progressBar = progressBar;  
22.     }  
23.   
24.   
25.     /**  
26.      * 这里的Integer参数对应AsyncTask中的第一个参数   
27.      * 这里的String返回值对应AsyncTask的第三个参数  
28.      * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改  
29.      * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作  
30.      */  
31.     @Override  
32.     protected String doInBackground(Integer... params) {  
33. netOperator = new NetOperator();  
34. i = 0;  
35. i = 10; i <= 100; i+=10) {  
36.             netOperator.operator();  
37.             publishProgress(i);  
38.         }  
39.         return i + params[0].intValue() + "";  
40.     }  
41.   
42.   
43.     /**  
44.      * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)  
45.      * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置  
46.      */  
47.     @Override  
48.     protected void onPostExecute(String result) {  
49.         textView.setText("异步操作执行结束" + result);  
50.     }  
51.   
52.   
53.     //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置  
54.     @Override  
55.     protected void onPreExecute() {  
56.         textView.setText("开始执行异步线程");  
57.     }  
58.   
59.   
60.     /**  
61.      * 这里的Intege参数对应AsyncTask中的第二个参数  
62.      * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行  
63.      * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作  
64.      */  
65.     @Override  
66.     protected void onProgressUpdate(Integer... values) {  
67. vlaue = values[0];  
68.         progressBar.setProgress(vlaue);  
69.     }  
70.   
71.       
72.       
73.       
74.   
75. }



详解Android中AsyncTask的使用




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

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

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

先来看看AsyncTask的定义:

 public abstract class AsyncTask<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的代码:

1. package com.scott.async;  
2.   
3. import java.io.ByteArrayOutputStream;  
4. import java.io.InputStream;  
5.   
6. import org.apache.http.HttpEntity;  
7. import org.apache.http.HttpResponse;  
8. import org.apache.http.HttpStatus;  
9. import org.apache.http.client.HttpClient;  
10. import org.apache.http.client.methods.HttpGet;  
11. import org.apache.http.impl.client.DefaultHttpClient;  
12.   
13. import android.app.Activity;  
14. import android.os.AsyncTask;  
15. import android.os.Bundle;  
16. import android.util.Log;  
17. import android.view.View;  
18. import android.widget.Button;  
19. import android.widget.ProgressBar;  
20. import android.widget.TextView;  
21.   
22. public class MainActivity extends Activity {  
23.       
24. private static final String TAG = "ASYNC_TASK";  
25.       
26. private Button execute;  
27. private Button cancel;  
28. private ProgressBar progressBar;  
29. private TextView textView;  
30.       
31. private MyTask mTask;  
32.       
33. @Override  
34. public void onCreate(Bundle savedInstanceState) {  
35. super.onCreate(savedInstanceState);  
36.         setContentView(R.layout.main);  
37.           
38.         execute = (Button) findViewById(R.id.execute);  
39. new View.OnClickListener() {  
40. @Override  
41. public void onClick(View v) {  
42. //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常  
43. new MyTask();  
44. "http://www.baidu.com");  
45.                   
46. false);  
47. true);  
48.             }  
49.         });  
50.         cancel = (Button) findViewById(R.id.cancel);  
51. new View.OnClickListener() {  
52. @Override  
53. public void onClick(View v) {  
54. //取消一个正在执行的任务,onCancelled方法将会被调用  
55. true);  
56.             }  
57.         });  
58.         progressBar = (ProgressBar) findViewById(R.id.progress_bar);  
59.         textView = (TextView) findViewById(R.id.text_view);  
60.           
61.     }  
62.       
63. private class MyTask extends AsyncTask<String, Integer, String> {  
64. //onPreExecute方法用于在执行后台任务前做一些UI操作  
65. @Override  
66. protected void onPreExecute() {  
67. "onPreExecute() called");  
68. "loading...");  
69.         }  
70.           
71. //doInBackground方法内部执行后台任务,不可在此方法内修改UI  
72. @Override  
73. protected String doInBackground(String... params) {  
74. "doInBackground(Params... params) called");  
75. try {  
76. new DefaultHttpClient();  
77. new HttpGet(params[0]);  
78.                 HttpResponse response = client.execute(get);  
79. if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
80.                     HttpEntity entity = response.getEntity();  
81.                     InputStream is = entity.getContent();  
82. long total = entity.getContentLength();  
83. new ByteArrayOutputStream();  
84. byte[] buf = new byte[1024];  
85. int count = 0;  
86. int length = -1;  
87. while ((length = is.read(buf)) != -1) {  
88. 0, length);  
89.                         count += length;  
90. //调用publishProgress公布进度,最后onProgressUpdate方法将被执行  
91. int) ((count / (float) total) * 100));  
92. //为了演示进度,休眠500毫秒  
93. 500);  
94.                     }  
95. return new String(baos.toByteArray(), "gb2312");  
96.                 }  
97. catch (Exception e) {  
98.                 Log.e(TAG, e.getMessage());  
99.             }  
100. return null;  
101.         }  
102.           
103. //onProgressUpdate方法用于更新进度信息  
104. @Override  
105. protected void onProgressUpdate(Integer... progresses) {  
106. "onProgressUpdate(Progress... progresses) called");  
107. 0]);  
108. "loading..." + progresses[0] + "%");  
109.         }  
110.           
111. //onPostExecute方法用于在执行完后台任务后更新UI,显示结果  
112. @Override  
113. protected void onPostExecute(String result) {  
114. "onPostExecute(Result result) called");  
115.             textView.setText(result);  
116.               
117. true);  
118. false);  
119.         }  
120.           
121. //onCancelled方法用于在取消执行中的任务时更改UI  
122. @Override  
123. protected void onCancelled() {  
124. "onCancelled() called");  
125. "cancelled");  
126. 0);  
127.               
128. true);  
129. false);  
130.         }  
131.     }  
132. }


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

1. <?xml version="1.0" encoding="utf-8"?>  
2. <LinearLayout xmlns: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中加入访问网络的权限:

1. <uses-permission android: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枚举类代码段如下:

1.         //初始状态  
2. private volatile Status mStatus = Status.PENDING;  
3.       
4. public enum Status {  
5. /**
6.          * Indicates that the task has not been executed yet.
7.          */  
8.         PENDING,  
9. /**
10.          * Indicates that the task is running.
11.          */  
12.         RUNNING,  
13. /**
14.          * Indicates that {@link AsyncTask#onPostExecute} has finished.
15.          */  
16.         FINISHED,  
17.     }  
18.   
19. /**
20.      * Returns the current status of this task.
21.      *
22.      * @return The current status.
23.      */  
24. public final Status getStatus() {  
25. return mStatus;  
26.     }


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

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

1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
2. if (mStatus != Status.PENDING) {  
3. switch (mStatus) {  
4. case RUNNING:  
5. //如果该任务正在被执行则抛出异常  
6. //值得一提的是,在调用cancel取消任务后,状态仍未RUNNING  
7. throw new IllegalStateException("Cannot execute task:"  
8. " the task is already running.");  
9. case FINISHED:  
10. //如果该任务已经执行完成则抛出异常  
11. throw new IllegalStateException("Cannot execute task:"  
12. " the task has already been executed "  
13. "(a task can be executed only once)");  
14.             }  
15.         }  
16.           
17. //改变状态为RUNNING  
18.         mStatus = Status.RUNNING;  
19.   
20. //调用onPreExecute方法  
21.         onPreExecute();  
22.   
23.         mWorker.mParams = params;  
24.         sExecutor.execute(mFuture);  
25.   
26. return this;  
27.     }

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

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


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. //新建一个队列用来存放线程  
6. private static final BlockingQueue<Runnable> sWorkQueue =  
7. new LinkedBlockingQueue<Runnable>(10);  
8. //新建一个线程工厂  
9. private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
10. private final AtomicInteger mCount = new AtomicInteger(1);  
11. //新建一个线程  
12. public Thread newThread(Runnable r) {  
13. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
14.        }  
15.    };  
16. //新建一个线程池执行器,用于管理线程的执行  
17. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
18.            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);


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


1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
2.         Params[] mParams;  
3.     }


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

1. /**
2.  * A cancellable asynchronous computation.
3.  * ...
4.  */  
5. public class FutureTask<V> implements RunnableFuture<V> {


1. public interface RunnableFuture<V> extends Runnable, Future<V> {  
2. /**
3.      * Sets this Future to the result of its computation
4.      * unless it has been cancelled.
5.      */  
6. void run();  
7. }

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

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


1.    private final WorkerRunnable<Params, Result> mWorker;  
2. private final FutureTask<Result> mFuture;  
3.   
4. public AsyncTask() {  
5. new WorkerRunnable<Params, Result>() {  
6. //call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法  
7. public Result call() throws Exception {  
8.                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
9. return doInBackground(mParams);  
10.            }  
11.        };  
12.   
13. //在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法  
14. new FutureTask<Result>(mWorker) {  
15. @Override  
16. protected void done() {  
17.                Message message;  
18. null;  
19.   
20. try {  
21.                    result = get();  
22. catch (InterruptedException e) {  
23.                    android.util.Log.w(LOG_TAG, e);  
24. catch (ExecutionException e) {  
25. throw new RuntimeException("An error occured while executing doInBackground()",  
26.                            e.getCause());  
27. catch (CancellationException e) {  
28. //发送取消任务的消息  
29.                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
30. new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
31.                    message.sendToTarget();  
32. return;  
33. catch (Throwable t) {  
34. throw new RuntimeException("An error occured while executing "  
35. "doInBackground()", t);  
36.                }  
37.   
38. //发送显示结果的消息  
39.                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
40. new AsyncTaskResult<Result>(AsyncTask.this, result));  
41.                message.sendToTarget();  
42.            }  
43.        };  
44.    }


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

1. private static final int MESSAGE_POST_RESULT = 0x1; //显示结果  
2. private static final int MESSAGE_POST_PROGRESS = 0x2;    //更新进度  
3. private static final int MESSAGE_POST_CANCEL = 0x3;  //取消任务  
4.   
5. private static final InternalHandler sHandler = new InternalHandler();  
6.   
7. private static class InternalHandler extends Handler {  
8. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
9. @Override  
10. public void handleMessage(Message msg) {  
11.            AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
12. switch (msg.what) {  
13. case MESSAGE_POST_RESULT:  
14. // There is only one result  
15. //调用AsyncTask.finish方法  
16. 0]);  
17. break;  
18. case MESSAGE_POST_PROGRESS:  
19. //调用AsyncTask.onProgressUpdate方法  
20.                 result.mTask.onProgressUpdate(result.mData);  
21. break;  
22. case MESSAGE_POST_CANCEL:  
23. //调用AsyncTask.onCancelled方法  
24.                    result.mTask.onCancelled();  
25. break;  
26.            }  
27.        }  
28.    }

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


1. private void finish(Result result) {  
2. if (isCancelled()) result = null;  
3. //调用onPostExecute显示结果  
4. //改变状态为FINISHED  
5.     }


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

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


1. AsyncTaskResult result = (AsyncTaskResult) msg.obj;

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


1. @SuppressWarnings({"RawUseOfParameterizedType"})  
2. private static class AsyncTaskResult<Data> {  
3. final AsyncTask mTask;  
4. final Data[] mData;  
5.   
6.     AsyncTaskResult(AsyncTask task, Data... data) {  
7.         mTask = task;  
8.         mData = data;  
9.     }  
10. }


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

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

1. //发送显示结果的消息  
2. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
3. new AsyncTaskResult<Result>(AsyncTask.this, result));  
4. message.sendToTarget();

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

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


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的良好封装,减少了开发者处理问题的复杂度,提高了开发效率,希望朋友们能多多体会一下。




[Android] AsyncTask使用实例---加载网络图片

先上效果图。如demo_asynctask.gif


android AsyncTask介绍 详解AsyncTask的使用  使用实例---加载网络图片_Android

对于图片的加载效果,见链接:[Android] PorterDuff使用实例----实现新浪微博图片下载效果

本文参考链接:http://developer.android.com/reference/android/os/AsyncTask.html

AsyncTask被设计成方便编写Thread与Handler交互的辅助类,几秒钟的背景耗时操作是理想的使用场合。
AsyncTask必须被子类化才能使用,在该过程中必须设定3个构造参数:
           1.Params 往背景线程执行时的参数类型
           2.Progress 背景线程发布任务进度的参数类型
           3.Result 背景线程的运行后最后返回的结果的参数类型
如果不需要声明参数类型,可以使用"Void"进行替代。

通常子类化声明代码:



    1. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap>

    不需要参数的声明代码:


      1. public class DownloadImgTask extends AsyncTask<Void, Void, Void>


      当一个AsyncTask被执行后,会有如下4个步骤:

                 1.onPreExecute() 在UI线程中调用。这个步骤通常用于一些准备操作,如显示一个进度条

                 2.doInBackground(Params ...) 当onPreExecute()执行完毕后,即被背景线程调用执行。这个步骤用于执行耗时的计算。方法中为不定参数,由外部调用AsyncTask.execute(Params...)时设定。本步骤运行的过程可以通过publishProgress(Progress...)发布到onProgressUpdate(Progress...)中,运行结果将return,后被传参至onPostExecute(Result)。

                 3.onProgressUpdate(Progress...) 当执行publishProgress(Progress...)后在UI线程中被调用。执行的时间是不确定的。

                 4.onPostExecute(Result) 在UI线程中被调用。参数为doInBackground(Params...)的运行结果。


      在AsyncTask的执行过程中可以调用cancel(boolean),方法中的参数值为false时允许已经开始工作的背景线程继续工作至任务完成;为true时则强制停止。AsyncTask将不再调用onPostExecute(Result),取而代之的是调用onCancelled(Object)。为了确定当前的AsyncTask是否已经被cancelled,应该在doInBackground(Params...)方法中执行isCancelled()进行检查。



      为了AsyncTask顺利工作,必须遵守线程规则:

                 1.AsyncTask必须在UI线程中被调用。JELLY_BEAN版本默认已经遵守此规则。

                 2.AsyncTask的实例必须在UI线程中被创建。

                 3.execute(Params...)必须在UI线程中被调用。

                 4.不允许手工调用以下方法:

                            onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)

                 5.AsyncTask的实例只能被执行一次。再次执行时将会抛出异常:

                            java.lang.IllegalStateException: Cannot execute task: the task is already running.

      Java代码贴上,XML请各位看官自行实现:

      1. ActAsyncTask.java  
      2.   
      3. package lab.sodino.asynctask;  
      4.   
      5. import android.app.Activity;  
      6. import android.os.Bundle;  
      7. import android.view.View;  
      8. import android.view.View.OnClickListener;  
      9.   
      10. public class ActAsyncTask extends Activity implements OnClickListener {  
      11. private PorterDuffView pViewA, pViewB, pViewC, pViewD;  
      12. public static final String[] STRING_ARR = {//  
      13. "http://developer.android.com/images/home/android-jellybean.png",//  
      14. "http://developer.android.com/images/home/design.png",//  
      15. "http://developer.android.com/images/home/google-play.png",//  
      16. "http://developer.android.com/images/home/google-io.png" };  
      17.   
      18. public void onCreate(Bundle savedInstanceState) {  
      19. super.onCreate(savedInstanceState);  
      20.         setContentView(R.layout.main);  
      21.   
      22.         pViewA = (PorterDuffView) findViewById(R.id.pViewA);  
      23. this);  
      24.         pViewB = (PorterDuffView) findViewById(R.id.pViewB);  
      25. this);  
      26.         pViewC = (PorterDuffView) findViewById(R.id.pViewC);  
      27. this);  
      28.         pViewD = (PorterDuffView) findViewById(R.id.pViewD);  
      29. this);  
      30.     }  
      31.   
      32. public void onClick(View v) {  
      33. if (v instanceof PorterDuffView) {  
      34.             PorterDuffView pdView = (PorterDuffView) v;  
      35. if (pdView.isLoading() == false) {  
      36. new DownloadImgTask(pdView);  
      37.                 task.execute(STRING_ARR[pdView.getId() % STRING_ARR.length]);  
      38. true);  
      39. true);  
      40. 0);  
      41.                 pdView.invalidate();  
      42.             }  
      43.         }  
      44.     }  
      45. }

      1. DownloadImgTask.java  
      2.   
      3.   
      4. package lab.sodino.asynctask;  
      5.   
      6. import java.io.ByteArrayOutputStream;  
      7. import java.io.IOException;  
      8. import java.io.InputStream;  
      9.   
      10. import org.apache.http.Header;  
      11. import org.apache.http.HttpEntity;  
      12. import org.apache.http.HttpResponse;  
      13. import org.apache.http.client.ClientProtocolException;  
      14. import org.apache.http.client.HttpClient;  
      15. import org.apache.http.client.methods.HttpGet;  
      16. import org.apache.http.impl.client.DefaultHttpClient;  
      17. import org.apache.http.params.HttpParams;  
      18.   
      19. import android.graphics.Bitmap;  
      20. import android.graphics.BitmapFactory;  
      21. import android.os.AsyncTask;  
      22.   
      23. /**
      24.  * @author Sodino 
      25.  * @version Time:2012-7-5 上午03:34:58
      26.  */  
      27. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap> {  
      28. private PorterDuffView pdView;  
      29.   
      30. public DownloadImgTask(PorterDuffView pdView) {  
      31. this.pdView = pdView;  
      32.     }  
      33.   
      34. /** 下载准备工作。在UI线程中调用。 */  
      35. protected void onPreExecute() {  
      36. this, "onPreExecute");  
      37.     }  
      38.   
      39. /** 执行下载。在背景线程调用。 */  
      40. protected Bitmap doInBackground(String... params) {  
      41. this, "doInBackground:" + params[0]);  
      42. new DefaultHttpClient();  
      43. new HttpGet(params[0]);  
      44. null;  
      45. null;  
      46. try {  
      47.             HttpResponse httpResponse = httpClient.execute(httpGet);  
      48.             printHttpResponse(httpResponse);  
      49.             HttpEntity httpEntity = httpResponse.getEntity();  
      50. long length = httpEntity.getContentLength();  
      51. this, "content length=" + length);  
      52.             is = httpEntity.getContent();  
      53. if (is != null) {  
      54. new ByteArrayOutputStream();  
      55. byte[] buf = new byte[128];  
      56. int read = -1;  
      57. int count = 0;  
      58. while ((read = is.read(buf)) != -1) {  
      59. 0, read);  
      60.                     count += read;  
      61. 1.0f / length);  
      62.                 }  
      63. this, "count=" + count + " length=" + length);  
      64. byte[] data = baos.toByteArray();  
      65. 0, data.length);  
      66. return bit;  
      67.             }  
      68. catch (ClientProtocolException e) {  
      69.             e.printStackTrace();  
      70. catch (IOException e) {  
      71.             e.printStackTrace();  
      72. finally {  
      73. try {  
      74. if (baos != null) {  
      75.                     baos.close();  
      76.                 }  
      77. if (is != null) {  
      78.                     is.close();  
      79.                 }  
      80. catch (IOException e) {  
      81.                 e.printStackTrace();  
      82.             }  
      83.         }  
      84. return null;  
      85.     }  
      86.   
      87. /** 更新下载进度。在UI线程调用。onProgressUpdate */  
      88. protected void onProgressUpdate(Float... progress) {  
      89. // LogOut.out(this, "onProgressUpdate");  
      90. 0]);  
      91.     }  
      92.   
      93. /** 通知下载任务完成。在UI线程调用。 */  
      94. protected void onPostExecute(Bitmap bit) {  
      95. this, "onPostExecute");  
      96. false);  
      97. false);  
      98.         pdView.setImageBitmap(bit);  
      99.     }  
      100.   
      101. protected void onCancelled() {  
      102. this, "DownloadImgTask cancel...");  
      103. super.onCancelled();  
      104.     }  
      105.   
      106. private void printHttpResponse(HttpResponse httpResponse) {  
      107.         Header[] headerArr = httpResponse.getAllHeaders();  
      108. for (int i = 0; i < headerArr.length; i++) {  
      109.             Header header = headerArr[i];  
      110. this, "name[" + header.getName() + "]value[" + header.getValue() + "]");  
      111.         }  
      112.         HttpParams params = httpResponse.getParams();  
      113. this, String.valueOf(params));  
      114. this, String.valueOf(httpResponse.getLocale()));  
      115.     }  
      116. }

      1. PorterDuffView.java  
      2.   
      3. package lab.sodino.asynctask;  
      4.   
      5. import java.text.DecimalFormat;  
      6.   
      7. import android.content.Context;  
      8. import android.content.res.TypedArray;  
      9. import android.graphics.Bitmap;  
      10. import android.graphics.Canvas;  
      11. import android.graphics.Paint;  
      12. import android.graphics.PorterDuff;  
      13. import android.graphics.PorterDuffXfermode;  
      14. import android.graphics.drawable.BitmapDrawable;  
      15. import android.graphics.drawable.Drawable;  
      16. import android.util.AttributeSet;  
      17. import android.widget.ImageView;  
      18.   
      19. /**
      20.  * 自定义组件实现新浪微博的图片加载效果。<br/>
      21.  * 
      22.  * @author Sodino 
      23.  * @version Time:2012-7-9 上午01:55:04
      24.  */  
      25. public class PorterDuffView extends ImageView {  
      26. /** 前景Bitmap高度为1像素。采用循环多次填充进度区域。 */  
      27. public static final int FG_HEIGHT = 1;  
      28. /** 下载进度前景色 */  
      29. // public static final int FOREGROUND_COLOR = 0x77123456;  
      30. public static final int FOREGROUND_COLOR = 0x77ff0000;  
      31. /** 下载进度条的颜色。 */  
      32. public static final int TEXT_COLOR = 0xff7fff00;  
      33. /** 进度百分比字体大小。 */  
      34. public static final int FONT_SIZE = 30;  
      35. private Bitmap bitmapBg, bitmapFg;  
      36. private Paint paint;  
      37. /** 标识当前进度。 */  
      38. private float progress;  
      39. /** 标识进度图片的宽度与高度。 */  
      40. private int width, height;  
      41. /** 格式化输出百分比。 */  
      42. private DecimalFormat decFormat;  
      43. /** 进度百分比文本的锚定Y中心坐标值。 */  
      44. private float txtBaseY;  
      45. /** 标识是否使用PorterDuff模式重组界面。 */  
      46. private boolean porterduffMode;  
      47. /** 标识是否正在下载图片。 */  
      48. private boolean loading;  
      49.   
      50. public PorterDuffView(Context context, AttributeSet attrs) {  
      51. super(context, attrs);  
      52.         init(context, attrs);  
      53.     }  
      54.   
      55. /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */  
      56. private static Bitmap createForegroundBitmap(int w) {  
      57.         Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);  
      58. new Canvas(bm);  
      59. new Paint(Paint.ANTI_ALIAS_FLAG);  
      60.         p.setColor(FOREGROUND_COLOR);  
      61. 0, 0, w, FG_HEIGHT, p);  
      62. return bm;  
      63.     }  
      64.   
      65. private void init(Context context, AttributeSet attrs) {  
      66. if (attrs != null) {  
      67. // //  
      68. // int count = attrs.getAttributeCount();  
      69. // for (int i = 0; i < count; i++) {  
      70. // LogOut.out(this, "attrNameRes:" +  
      71. // Integer.toHexString(attrs.getAttributeNameResource(i))//  
      72. // + " attrName:" + attrs.getAttributeName(i)//  
      73. // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//  
      74. // + " attrValue:" + attrs.getAttributeValue(i)//  
      75. // );  
      76. // }  
      77. // //  
      78.   
      79.             TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);  
      80. false);  
      81.         }  
      82.         Drawable drawable = getDrawable();  
      83. if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {  
      84.             bitmapBg = ((BitmapDrawable) drawable).getBitmap();  
      85.             width = bitmapBg.getWidth();  
      86.             height = bitmapBg.getHeight();  
      87. // LogOut.out(this, "width=" + width + " height=" + height);  
      88.             bitmapFg = createForegroundBitmap(width);  
      89. else {  
      90. // 不符合要求,自动设置为false。  
      91. false;  
      92.         }  
      93.   
      94. new Paint();  
      95. false);  
      96. true);  
      97.         paint.setTextSize(FONT_SIZE);  
      98.   
      99. // 关于FontMetrics的详情介绍,可见:  
      100. // http://xxxxxfsadf.iteye.com/blog/480454  
      101.         Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
      102. // 注意观察本输出:  
      103. // ascent:单个字符基线以上的推荐间距,为负数  
      104. this, "ascent:" + fontMetrics.ascent//  
      105. // descent:单个字符基线以下的推荐间距,为正数  
      106. " descent:" + fontMetrics.descent //  
      107. // 单个字符基线以上的最大间距,为负数  
      108. " top:" + fontMetrics.top //  
      109. // 单个字符基线以下的最大间距,为正数  
      110. " bottom:" + fontMetrics.bottom//  
      111. // 文本行与行之间的推荐间距  
      112. " leading:" + fontMetrics.leading);  
      113. // 在此处直接计算出来,避免了在onDraw()处的重复计算  
      114. 2;  
      115.   
      116. new DecimalFormat("0.0%");  
      117.     }  
      118.   
      119. public void onDraw(Canvas canvas) {  
      120. if (porterduffMode) {  
      121. int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;  
      122. // 画出背景图  
      123.             canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);  
      124. // 设置PorterDuff模式  
      125. new PorterDuffXfermode(PorterDuff.Mode.DARKEN));  
      126. // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,  
      127. // paint);  
      128. int tH = height - (int) (progress * height);  
      129. for (int i = 0; i < tH; i++) {  
      130.                 canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);  
      131.             }  
      132.   
      133. // 立即取消xfermode  
      134. null);  
      135. int oriColor = paint.getColor();  
      136.             paint.setColor(TEXT_COLOR);  
      137.             paint.setTextSize(FONT_SIZE);  
      138.             String tmp = decFormat.format(progress);  
      139. float tmpWidth = paint.measureText(tmp);  
      140. 2, tmpH + txtBaseY, paint);  
      141. // 恢复为初始值时的颜色  
      142.             paint.setColor(oriColor);  
      143. else {  
      144. this, "onDraw super");  
      145. super.onDraw(canvas);  
      146.         }  
      147.     }  
      148.   
      149. public void setProgress(float progress) {  
      150. if (porterduffMode) {  
      151. this.progress = progress;  
      152. // 刷新自身。  
      153.             invalidate();  
      154.         }  
      155.     }  
      156.   
      157. public void setBitmap(Bitmap bg) {  
      158. if (porterduffMode) {  
      159.             bitmapBg = bg;  
      160.             width = bitmapBg.getWidth();  
      161.             height = bitmapBg.getHeight();  
      162.   
      163.             bitmapFg = createForegroundBitmap(width);  
      164.   
      165.             Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
      166. 2;  
      167.               
      168.             setImageBitmap(bg);  
      169. // 请求重新布局,将会再次调用onMeasure()  
      170. // requestLayout();  
      171.         }  
      172.     }  
      173.   
      174. public boolean isLoading() {  
      175. return loading;  
      176.     }  
      177.   
      178. public void setLoading(boolean loading) {  
      179. this.loading = loading;  
      180.     }  
      181.   
      182. public void setPorterDuffMode(boolean bool) {  
      183.         porterduffMode = bool;  
      184.     }  
      185. }





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

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

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

      先来看看AsyncTask的定义:


      1. public abstract class AsyncTask<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的代码:


      1. package com.scott.async;  
      2.   
      3. import java.io.ByteArrayOutputStream;  
      4. import java.io.InputStream;  
      5.   
      6. import org.apache.http.HttpEntity;  
      7. import org.apache.http.HttpResponse;  
      8. import org.apache.http.HttpStatus;  
      9. import org.apache.http.client.HttpClient;  
      10. import org.apache.http.client.methods.HttpGet;  
      11. import org.apache.http.impl.client.DefaultHttpClient;  
      12.   
      13. import android.app.Activity;  
      14. import android.os.AsyncTask;  
      15. import android.os.Bundle;  
      16. import android.util.Log;  
      17. import android.view.View;  
      18. import android.widget.Button;  
      19. import android.widget.ProgressBar;  
      20. import android.widget.TextView;  
      21.   
      22. public class MainActivity extends Activity {  
      23.       
      24. private static final String TAG = "ASYNC_TASK";  
      25.       
      26. private Button execute;  
      27. private Button cancel;  
      28. private ProgressBar progressBar;  
      29. private TextView textView;  
      30.       
      31. private MyTask mTask;  
      32.       
      33. @Override  
      34. public void onCreate(Bundle savedInstanceState) {  
      35. super.onCreate(savedInstanceState);  
      36.         setContentView(R.layout.main);  
      37.           
      38.         execute = (Button) findViewById(R.id.execute);  
      39. new View.OnClickListener() {  
      40. @Override  
      41. public void onClick(View v) {  
      42. //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常  
      43. new MyTask();  
      44. "http://www.baidu.com");  
      45.                   
      46. false);  
      47. true);  
      48.             }  
      49.         });  
      50.         cancel = (Button) findViewById(R.id.cancel);  
      51. new View.OnClickListener() {  
      52. @Override  
      53. public void onClick(View v) {  
      54. //取消一个正在执行的任务,onCancelled方法将会被调用  
      55. true);  
      56.             }  
      57.         });  
      58.         progressBar = (ProgressBar) findViewById(R.id.progress_bar);  
      59.         textView = (TextView) findViewById(R.id.text_view);  
      60.           
      61.     }  
      62.       
      63. private class MyTask extends AsyncTask<String, Integer, String> {  
      64. //onPreExecute方法用于在执行后台任务前做一些UI操作  
      65. @Override  
      66. protected void onPreExecute() {  
      67. "onPreExecute() called");  
      68. "loading...");  
      69.         }  
      70.           
      71. //doInBackground方法内部执行后台任务,不可在此方法内修改UI  
      72. @Override  
      73. protected String doInBackground(String... params) {  
      74. "doInBackground(Params... params) called");  
      75. try {  
      76. new DefaultHttpClient();  
      77. new HttpGet(params[0]);  
      78.                 HttpResponse response = client.execute(get);  
      79. if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {  
      80.                     HttpEntity entity = response.getEntity();  
      81.                     InputStream is = entity.getContent();  
      82. long total = entity.getContentLength();  
      83. new ByteArrayOutputStream();  
      84. byte[] buf = new byte[1024];  
      85. int count = 0;  
      86. int length = -1;  
      87. while ((length = is.read(buf)) != -1) {  
      88. 0, length);  
      89.                         count += length;  
      90. //调用publishProgress公布进度,最后onProgressUpdate方法将被执行  
      91. int) ((count / (float) total) * 100));  
      92. //为了演示进度,休眠500毫秒  
      93. 500);  
      94.                     }  
      95. return new String(baos.toByteArray(), "gb2312");  
      96.                 }  
      97. catch (Exception e) {  
      98.                 Log.e(TAG, e.getMessage());  
      99.             }  
      100. return null;  
      101.         }  
      102.           
      103. //onProgressUpdate方法用于更新进度信息  
      104. @Override  
      105. protected void onProgressUpdate(Integer... progresses) {  
      106. "onProgressUpdate(Progress... progresses) called");  
      107. 0]);  
      108. "loading..." + progresses[0] + "%");  
      109.         }  
      110.           
      111. //onPostExecute方法用于在执行完后台任务后更新UI,显示结果  
      112. @Override  
      113. protected void onPostExecute(String result) {  
      114. "onPostExecute(Result result) called");  
      115.             textView.setText(result);  
      116.               
      117. true);  
      118. false);  
      119.         }  
      120.           
      121. //onCancelled方法用于在取消执行中的任务时更改UI  
      122. @Override  
      123. protected void onCancelled() {  
      124. "onCancelled() called");  
      125. "cancelled");  
      126. 0);  
      127.               
      128. true);  
      129. false);  
      130.         }  
      131.     }  
      132. }

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

           
           
              
      1. <?xml version="1.0" encoding="utf-8"?>  
      2. <LinearLayout xmlns: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中加入访问网络的权限:


      1. <uses-permission android: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枚举类代码段如下:


           
           
              
      1.         //初始状态  
      2. private volatile Status mStatus = Status.PENDING;  
      3.       
      4. public enum Status {  
      5. /**
      6.          * Indicates that the task has not been executed yet.
      7.          */  
      8.         PENDING,  
      9. /**
      10.          * Indicates that the task is running.
      11.          */  
      12.         RUNNING,  
      13. /**
      14.          * Indicates that {@link AsyncTask#onPostExecute} has finished.
      15.          */  
      16.         FINISHED,  
      17.     }  
      18.   
      19. /**
      20.      * Returns the current status of this task.
      21.      *
      22.      * @return The current status.
      23.      */  
      24. public final Status getStatus() {  
      25. return mStatus;  
      26.     }

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

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


      1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
      2. if (mStatus != Status.PENDING) {  
      3. switch (mStatus) {  
      4. case RUNNING:  
      5. //如果该任务正在被执行则抛出异常  
      6. //值得一提的是,在调用cancel取消任务后,状态仍未RUNNING  
      7. throw new IllegalStateException("Cannot execute task:"  
      8. " the task is already running.");  
      9. case FINISHED:  
      10. //如果该任务已经执行完成则抛出异常  
      11. throw new IllegalStateException("Cannot execute task:"  
      12. " the task has already been executed "  
      13. "(a task can be executed only once)");  
      14.             }  
      15.         }  
      16.           
      17. //改变状态为RUNNING  
      18.         mStatus = Status.RUNNING;  
      19.   
      20. //调用onPreExecute方法  
      21.         onPreExecute();  
      22.   
      23.         mWorker.mParams = params;  
      24.         sExecutor.execute(mFuture);  
      25.   
      26. return this;  
      27.     }

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

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

        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. //新建一个队列用来存放线程  
        6. private static final BlockingQueue<Runnable> sWorkQueue =  
        7. new LinkedBlockingQueue<Runnable>(10);  
        8. //新建一个线程工厂  
        9. private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
        10. private final AtomicInteger mCount = new AtomicInteger(1);  
        11. //新建一个线程  
        12. public Thread newThread(Runnable r) {  
        13. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
        14.        }  
        15.    };  
        16. //新建一个线程池执行器,用于管理线程的执行  
        17. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
        18.            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);


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


          1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
          2.         Params[] mParams;  
          3.     }


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


            1. /**
            2.  * A cancellable asynchronous computation.
            3.  * ...
            4.  */  
            5. public class FutureTask<V> implements RunnableFuture<V> {


            1. public interface RunnableFuture<V> extends Runnable, Future<V> {  
            2. /**
            3.      * Sets this Future to the result of its computation
            4.      * unless it has been cancelled.
            5.      */  
            6. void run();  
            7. }


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

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

            1.    private final WorkerRunnable<Params, Result> mWorker;  
            2. private final FutureTask<Result> mFuture;  
            3.   
            4. public AsyncTask() {  
            5. new WorkerRunnable<Params, Result>() {  
            6. //call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法  
            7. public Result call() throws Exception {  
            8.                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
            9. return doInBackground(mParams);  
            10.            }  
            11.        };  
            12.   
            13. //在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法  
            14. new FutureTask<Result>(mWorker) {  
            15. @Override  
            16. protected void done() {  
            17.                Message message;  
            18. null;  
            19.   
            20. try {  
            21.                    result = get();  
            22. catch (InterruptedException e) {  
            23.                    android.util.Log.w(LOG_TAG, e);  
            24. catch (ExecutionException e) {  
            25. throw new RuntimeException("An error occured while executing doInBackground()",  
            26.                            e.getCause());  
            27. catch (CancellationException e) {  
            28. //发送取消任务的消息  
            29.                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,  
            30. new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));  
            31.                    message.sendToTarget();  
            32. return;  
            33. catch (Throwable t) {  
            34. throw new RuntimeException("An error occured while executing "  
            35. "doInBackground()", t);  
            36.                }  
            37.   
            38. //发送显示结果的消息  
            39.                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
            40. new AsyncTaskResult<Result>(AsyncTask.this, result));  
            41.                message.sendToTarget();  
            42.            }  
            43.        };  
            44.    }

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


            1. private static final int MESSAGE_POST_RESULT = 0x1; //显示结果  
            2. private static final int MESSAGE_POST_PROGRESS = 0x2;    //更新进度  
            3. private static final int MESSAGE_POST_CANCEL = 0x3;  //取消任务  
            4.   
            5. private static final InternalHandler sHandler = new InternalHandler();  
            6.   
            7. private static class InternalHandler extends Handler {  
            8. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  
            9. @Override  
            10. public void handleMessage(Message msg) {  
            11.            AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
            12. switch (msg.what) {  
            13. case MESSAGE_POST_RESULT:  
            14. // There is only one result  
            15. //调用AsyncTask.finish方法  
            16. 0]);  
            17. break;  
            18. case MESSAGE_POST_PROGRESS:  
            19. //调用AsyncTask.onProgressUpdate方法  
            20.                 result.mTask.onProgressUpdate(result.mData);  
            21. break;  
            22. case MESSAGE_POST_CANCEL:  
            23. //调用AsyncTask.onCancelled方法  
            24.                    result.mTask.onCancelled();  
            25. break;  
            26.            }  
            27.        }  
            28.    }

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

            1. private void finish(Result result) {  
            2. if (isCancelled()) result = null;  
            3. //调用onPostExecute显示结果  
            4. //改变状态为FINISHED  
            5.     }


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

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

            1. AsyncTaskResult result = (AsyncTaskResult) msg.obj;

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

                 
                 
                    
            1. @SuppressWarnings({"RawUseOfParameterizedType"})  
            2. private static class AsyncTaskResult<Data> {  
            3. final AsyncTask mTask;  
            4. final Data[] mData;  
            5.   
            6.     AsyncTaskResult(AsyncTask task, Data... data) {  
            7.         mTask = task;  
            8.         mData = data;  
            9.     }  
            10. }


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


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

            //发送显示结果的消息  
            1. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
            2. new AsyncTaskResult<Result>(AsyncTask.this, result));  
            3. message.sendToTarget();

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


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



                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的良好封装,减少了开发者处理问题的复杂度,提高了开发效率,希望朋友们能多多体会一下。

                先上效果图。如demo_asynctask.gif


                android AsyncTask介绍 详解AsyncTask的使用  使用实例---加载网络图片_Android

                对于图片的加载效果,见链接:[Android] PorterDuff使用实例----实现新浪微博图片下载效果

                本文参考链接:http://developer.android.com/reference/android/os/AsyncTask.html

                AsyncTask被设计成方便编写Thread与Handler交互的辅助类,几秒钟的背景耗时操作是理想的使用场合。
                AsyncTask必须被子类化才能使用,在该过程中必须设定3个构造参数:
                           1.Params 往背景线程执行时的参数类型
                           2.Progress 背景线程发布任务进度的参数类型
                           3.Result 背景线程的运行后最后返回的结果的参数类型
                如果不需要声明参数类型,可以使用"Void"进行替代。

                通常子类化声明代码:


                1. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap>

                不需要参数的声明代码:

                1. public class DownloadImgTask extends AsyncTask<Void, Void, Void>

                当一个AsyncTask被执行后,会有如下4个步骤:

                           1.onPreExecute() 在UI线程中调用。这个步骤通常用于一些准备操作,如显示一个进度条

                           2.doInBackground(Params ...) 当onPreExecute()执行完毕后,即被背景线程调用执行。这个步骤用于执行耗时的计算。方法中为不定参数,由外部调用AsyncTask.execute(Params...)时设定。本步骤运行的过程可以通过publishProgress(Progress...)发布到onProgressUpdate(Progress...)中,运行结果将return,后被传参至onPostExecute(Result)。

                           3.onProgressUpdate(Progress...) 当执行publishProgress(Progress...)后在UI线程中被调用。执行的时间是不确定的。

                           4.onPostExecute(Result) 在UI线程中被调用。参数为doInBackground(Params...)的运行结果。


                在AsyncTask的执行过程中可以调用cancel(boolean),方法中的参数值为false时允许已经开始工作的背景线程继续工作至任务完成;为true时则强制停止。AsyncTask将不再调用onPostExecute(Result),取而代之的是调用onCancelled(Object)。为了确定当前的AsyncTask是否已经被cancelled,应该在doInBackground(Params...)方法中执行isCancelled()进行检查。



                为了AsyncTask顺利工作,必须遵守线程规则:

                           1.AsyncTask必须在UI线程中被调用。JELLY_BEAN版本默认已经遵守此规则。

                           2.AsyncTask的实例必须在UI线程中被创建。

                           3.execute(Params...)必须在UI线程中被调用。

                           4.不允许手工调用以下方法:

                                      onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)

                           5.AsyncTask的实例只能被执行一次。再次执行时将会抛出异常:

                                      java.lang.IllegalStateException: Cannot execute task: the task is already running.

                Java代码贴上,XML请各位看官自行实现:

                1. ActAsyncTask.java  
                2.   
                3. package lab.sodino.asynctask;  
                4.   
                5. import android.app.Activity;  
                6. import android.os.Bundle;  
                7. import android.view.View;  
                8. import android.view.View.OnClickListener;  
                9.   
                10. public class ActAsyncTask extends Activity implements OnClickListener {  
                11. private PorterDuffView pViewA, pViewB, pViewC, pViewD;  
                12. public static final String[] STRING_ARR = {//  
                13. "http://developer.android.com/images/home/android-jellybean.png",//  
                14. "http://developer.android.com/images/home/design.png",//  
                15. "http://developer.android.com/images/home/google-play.png",//  
                16. "http://developer.android.com/images/home/google-io.png" };  
                17.   
                18. public void onCreate(Bundle savedInstanceState) {  
                19. super.onCreate(savedInstanceState);  
                20.         setContentView(R.layout.main);  
                21.   
                22.         pViewA = (PorterDuffView) findViewById(R.id.pViewA);  
                23. this);  
                24.         pViewB = (PorterDuffView) findViewById(R.id.pViewB);  
                25. this);  
                26.         pViewC = (PorterDuffView) findViewById(R.id.pViewC);  
                27. this);  
                28.         pViewD = (PorterDuffView) findViewById(R.id.pViewD);  
                29. this);  
                30.     }  
                31.   
                32. public void onClick(View v) {  
                33. if (v instanceof PorterDuffView) {  
                34.             PorterDuffView pdView = (PorterDuffView) v;  
                35. if (pdView.isLoading() == false) {  
                36. new DownloadImgTask(pdView);  
                37.                 task.execute(STRING_ARR[pdView.getId() % STRING_ARR.length]);  
                38. true);  
                39. true);  
                40. 0);  
                41.                 pdView.invalidate();  
                42.             }  
                43.         }  
                44.     }  
                45. }


                1. DownloadImgTask.java  
                2.   
                3.   
                4. package lab.sodino.asynctask;  
                5.   
                6. import java.io.ByteArrayOutputStream;  
                7. import java.io.IOException;  
                8. import java.io.InputStream;  
                9.   
                10. import org.apache.http.Header;  
                11. import org.apache.http.HttpEntity;  
                12. import org.apache.http.HttpResponse;  
                13. import org.apache.http.client.ClientProtocolException;  
                14. import org.apache.http.client.HttpClient;  
                15. import org.apache.http.client.methods.HttpGet;  
                16. import org.apache.http.impl.client.DefaultHttpClient;  
                17. import org.apache.http.params.HttpParams;  
                18.   
                19. import android.graphics.Bitmap;  
                20. import android.graphics.BitmapFactory;  
                21. import android.os.AsyncTask;  
                22.   
                23. /**
                24.  * @author Sodino
                25.  * @version Time:2012-7-5 上午03:34:58
                26.  */  
                27. public class DownloadImgTask extends AsyncTask<String, Float, Bitmap> {  
                28. private PorterDuffView pdView;  
                29.   
                30. public DownloadImgTask(PorterDuffView pdView) {  
                31. this.pdView = pdView;  
                32.     }  
                33.   
                34. /** 下载准备工作。在UI线程中调用。 */  
                35. protected void onPreExecute() {  
                36. this, "onPreExecute");  
                37.     }  
                38.   
                39. /** 执行下载。在背景线程调用。 */  
                40. protected Bitmap doInBackground(String... params) {  
                41. this, "doInBackground:" + params[0]);  
                42. new DefaultHttpClient();  
                43. new HttpGet(params[0]);  
                44. null;  
                45. null;  
                46. try {  
                47.             HttpResponse httpResponse = httpClient.execute(httpGet);  
                48.             printHttpResponse(httpResponse);  
                49.             HttpEntity httpEntity = httpResponse.getEntity();  
                50. long length = httpEntity.getContentLength();  
                51. this, "content length=" + length);  
                52.             is = httpEntity.getContent();  
                53. if (is != null) {  
                54. new ByteArrayOutputStream();  
                55. byte[] buf = new byte[128];  
                56. int read = -1;  
                57. int count = 0;  
                58. while ((read = is.read(buf)) != -1) {  
                59. 0, read);  
                60.                     count += read;  
                61. 1.0f / length);  
                62.                 }  
                63. this, "count=" + count + " length=" + length);  
                64. byte[] data = baos.toByteArray();  
                65. 0, data.length);  
                66. return bit;  
                67.             }  
                68. catch (ClientProtocolException e) {  
                69.             e.printStackTrace();  
                70. catch (IOException e) {  
                71.             e.printStackTrace();  
                72. finally {  
                73. try {  
                74. if (baos != null) {  
                75.                     baos.close();  
                76.                 }  
                77. if (is != null) {  
                78.                     is.close();  
                79.                 }  
                80. catch (IOException e) {  
                81.                 e.printStackTrace();  
                82.             }  
                83.         }  
                84. return null;  
                85.     }  
                86.   
                87. /** 更新下载进度。在UI线程调用。onProgressUpdate */  
                88. protected void onProgressUpdate(Float... progress) {  
                89. // LogOut.out(this, "onProgressUpdate");  
                90. 0]);  
                91.     }  
                92.   
                93. /** 通知下载任务完成。在UI线程调用。 */  
                94. protected void onPostExecute(Bitmap bit) {  
                95. this, "onPostExecute");  
                96. false);  
                97. false);  
                98.         pdView.setImageBitmap(bit);  
                99.     }  
                100.   
                101. protected void onCancelled() {  
                102. this, "DownloadImgTask cancel...");  
                103. super.onCancelled();  
                104.     }  
                105.   
                106. private void printHttpResponse(HttpResponse httpResponse) {  
                107.         Header[] headerArr = httpResponse.getAllHeaders();  
                108. for (int i = 0; i < headerArr.length; i++) {  
                109.             Header header = headerArr[i];  
                110. this, "name[" + header.getName() + "]value[" + header.getValue() + "]");  
                111.         }  
                112.         HttpParams params = httpResponse.getParams();  
                113. this, String.valueOf(params));  
                114. this, String.valueOf(httpResponse.getLocale()));  
                115.     }  
                116. }


                1. PorterDuffView.java  
                2.   
                3. package lab.sodino.asynctask;  
                4.   
                5. import java.text.DecimalFormat;  
                6.   
                7. import android.content.Context;  
                8. import android.content.res.TypedArray;  
                9. import android.graphics.Bitmap;  
                10. import android.graphics.Canvas;  
                11. import android.graphics.Paint;  
                12. import android.graphics.PorterDuff;  
                13. import android.graphics.PorterDuffXfermode;  
                14. import android.graphics.drawable.BitmapDrawable;  
                15. import android.graphics.drawable.Drawable;  
                16. import android.util.AttributeSet;  
                17. import android.widget.ImageView;  
                18.   
                19. /**
                20.  * 自定义组件实现新浪微博的图片加载效果。<br/>
                21.  * 
                22.  * @author Sodino
                23.  * @version Time:2012-7-9 上午01:55:04
                24.  */  
                25. public class PorterDuffView extends ImageView {  
                26. /** 前景Bitmap高度为1像素。采用循环多次填充进度区域。 */  
                27. public static final int FG_HEIGHT = 1;  
                28. /** 下载进度前景色 */  
                29. // public static final int FOREGROUND_COLOR = 0x77123456;  
                30. public static final int FOREGROUND_COLOR = 0x77ff0000;  
                31. /** 下载进度条的颜色。 */  
                32. public static final int TEXT_COLOR = 0xff7fff00;  
                33. /** 进度百分比字体大小。 */  
                34. public static final int FONT_SIZE = 30;  
                35. private Bitmap bitmapBg, bitmapFg;  
                36. private Paint paint;  
                37. /** 标识当前进度。 */  
                38. private float progress;  
                39. /** 标识进度图片的宽度与高度。 */  
                40. private int width, height;  
                41. /** 格式化输出百分比。 */  
                42. private DecimalFormat decFormat;  
                43. /** 进度百分比文本的锚定Y中心坐标值。 */  
                44. private float txtBaseY;  
                45. /** 标识是否使用PorterDuff模式重组界面。 */  
                46. private boolean porterduffMode;  
                47. /** 标识是否正在下载图片。 */  
                48. private boolean loading;  
                49.   
                50. public PorterDuffView(Context context, AttributeSet attrs) {  
                51. super(context, attrs);  
                52.         init(context, attrs);  
                53.     }  
                54.   
                55. /** 生成一宽与背景图片等同高为1像素的Bitmap,。 */  
                56. private static Bitmap createForegroundBitmap(int w) {  
                57.         Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);  
                58. new Canvas(bm);  
                59. new Paint(Paint.ANTI_ALIAS_FLAG);  
                60.         p.setColor(FOREGROUND_COLOR);  
                61. 0, 0, w, FG_HEIGHT, p);  
                62. return bm;  
                63.     }  
                64.   
                65. private void init(Context context, AttributeSet attrs) {  
                66. if (attrs != null) {  
                67. // //  
                68. // int count = attrs.getAttributeCount();  
                69. // for (int i = 0; i < count; i++) {  
                70. // LogOut.out(this, "attrNameRes:" +  
                71. // Integer.toHexString(attrs.getAttributeNameResource(i))//  
                72. // + " attrName:" + attrs.getAttributeName(i)//  
                73. // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//  
                74. // + " attrValue:" + attrs.getAttributeValue(i)//  
                75. // );  
                76. // }  
                77. // //  
                78.   
                79.             TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);  
                80. false);  
                81.         }  
                82.         Drawable drawable = getDrawable();  
                83. if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {  
                84.             bitmapBg = ((BitmapDrawable) drawable).getBitmap();  
                85.             width = bitmapBg.getWidth();  
                86.             height = bitmapBg.getHeight();  
                87. // LogOut.out(this, "width=" + width + " height=" + height);  
                88.             bitmapFg = createForegroundBitmap(width);  
                89. else {  
                90. // 不符合要求,自动设置为false。  
                91. false;  
                92.         }  
                93.   
                94. new Paint();  
                95. false);  
                96. true);  
                97.         paint.setTextSize(FONT_SIZE);  
                98.   
                99. // 关于FontMetrics的详情介绍,可见:  
                100. // http://xxxxxfsadf.iteye.com/blog/480454  
                101.         Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
                102. // 注意观察本输出:  
                103. // ascent:单个字符基线以上的推荐间距,为负数  
                104. this, "ascent:" + fontMetrics.ascent//  
                105. // descent:单个字符基线以下的推荐间距,为正数  
                106. " descent:" + fontMetrics.descent //  
                107. // 单个字符基线以上的最大间距,为负数  
                108. " top:" + fontMetrics.top //  
                109. // 单个字符基线以下的最大间距,为正数  
                110. " bottom:" + fontMetrics.bottom//  
                111. // 文本行与行之间的推荐间距  
                112. " leading:" + fontMetrics.leading);  
                113. // 在此处直接计算出来,避免了在onDraw()处的重复计算  
                114. 2;  
                115.   
                116. new DecimalFormat("0.0%");  
                117.     }  
                118.   
                119. public void onDraw(Canvas canvas) {  
                120. if (porterduffMode) {  
                121. int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;  
                122. // 画出背景图  
                123.             canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);  
                124. // 设置PorterDuff模式  
                125. new PorterDuffXfermode(PorterDuff.Mode.DARKEN));  
                126. // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,  
                127. // paint);  
                128. int tH = height - (int) (progress * height);  
                129. for (int i = 0; i < tH; i++) {  
                130.                 canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);  
                131.             }  
                132.   
                133. // 立即取消xfermode  
                134. null);  
                135. int oriColor = paint.getColor();  
                136.             paint.setColor(TEXT_COLOR);  
                137.             paint.setTextSize(FONT_SIZE);  
                138.             String tmp = decFormat.format(progress);  
                139. float tmpWidth = paint.measureText(tmp);  
                140. 2, tmpH + txtBaseY, paint);  
                141. // 恢复为初始值时的颜色  
                142.             paint.setColor(oriColor);  
                143. else {  
                144. this, "onDraw super");  
                145. super.onDraw(canvas);  
                146.         }  
                147.     }  
                148.   
                149. public void setProgress(float progress) {  
                150. if (porterduffMode) {  
                151. this.progress = progress;  
                152. // 刷新自身。  
                153.             invalidate();  
                154.         }  
                155.     }  
                156.   
                157. public void setBitmap(Bitmap bg) {  
                158. if (porterduffMode) {  
                159.             bitmapBg = bg;  
                160.             width = bitmapBg.getWidth();  
                161.             height = bitmapBg.getHeight();  
                162.   
                163.             bitmapFg = createForegroundBitmap(width);  
                164.   
                165.             Paint.FontMetrics fontMetrics = paint.getFontMetrics();  
                166. 2;  
                167.               
                168.             setImageBitmap(bg);  
                169. // 请求重新布局,将会再次调用onMeasure()  
                170. // requestLayout();  
                171.         }  
                172.     }  
                173.   
                174. public boolean isLoading() {  
                175. return loading;  
                176.     }  
                177.   
                178. public void setLoading(boolean loading) {  
                179. this.loading = loading;  
                180.     }  
                181.   
                182. public void setPorterDuffMode(boolean bool) {  
                183.         porterduffMode = bool;  
                184.     }  
                185. }