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
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TextView
- android:id="@+id/textView01"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
- <ProgressBar
- android:id="@+id/progressBar02"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- style="?android:attr/progressBarStyleHorizontal"
- />
- <Button
- android:id="@+id/button03"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="更新progressbar"
- />
- </LinearLayout>
MainActivity.java
- package vic.wong.main;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ProgressBar;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- private Button button;
- private ProgressBar progressBar;
- private TextView textView;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- button = (Button)findViewById(R.id.button03);
- progressBar = (ProgressBar)findViewById(R.id.progressBar02);
- textView = (TextView)findViewById(R.id.textView01);
- button.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(textView, progressBar);
- asyncTask.execute(1000);
- }
- });
- }
- }
NetOperator.java
- package vic.wong.main;
- //模拟網絡環境
- public class NetOperator {
- public void operator(){
- try {
- //休眠1秒
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
ProgressBarAsyncTask .java
- package vic.wong.main;
- import android.os.AsyncTask;
- import android.widget.ProgressBar;
- import android.widget.TextView;
- public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
- private TextView textView;
- private ProgressBar progressBar;
- public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
- super();
- this.textView = textView;
- this.progressBar = progressBar;
- }
- @Override
- protected String doInBackground(Integer... params) {
- NetOperator netOperator = new NetOperator();
- int i = 0;
- for (i = 10; i <= 100; i+=10) {
- netOperator.operator();
- publishProgress(i);
- }
- return i + params[0].intValue() + "";
- }
- @Override
- protected void onPostExecute(String result) {
- textView.setText("異步操作執行結束" + result);
- }
- //該方法運作在UI線程當中,并且運作在UI線程當中 可以對UI空間進行設定
- @Override
- protected void onPreExecute() {
- textView.setText("開始執行異步線程");
- }
- @Override
- protected void onProgressUpdate(Integer... values) {
- int vlaue = values[0];
- progressBar.setProgress(vlaue);
- }
- }
分類: Android之Handler 好文要頂 關注我 收藏該文
Devin Zhang
關注 - 0
粉絲 - 979 +加關注 35 0 (請您對文章做出評價) « 上一篇: Android常用知識點總彙
» 下一篇: Android消息推送
posted on 2012-02-13 22:05 Devin Zhang 閱讀( 212470) 評論( 22) 編輯 收藏
評論: #1樓 2012-03-25 23:56 | nickycookie
publicProgress好像應該是publishProgress 支援(2) 反對(0) #2樓 2012-08-28 22:03 | scwsmile
謝謝! 支援(0) 反對(0) #3樓 2012-09-20 16:52 | 孤城雪飄
講解的很清楚,感謝分享…… 支援(0) 反對(0) #4樓 2012-10-30 11:18 | singlecheng
樓主寫的不錯,比較用心,最好再貼上源碼。嘿嘿 支援(0) 反對(1) #5樓 2013-04-24 09:44 | renfujiang
•doInBackground(Params…) 背景執行,比較耗時的操作都可以放在這裡。注意這裡不能直接操作UI。此方法在背景線程執行,完成任務的主要工作,通常需要較長的時間。在執行過程中可以調用publicProgress(Progress…)來更新任務的進度。執行中遇到異常 怎麼通知呢? 支援(0) 反對(0) #6樓 2013-04-27 15:23 | 燊仔
@singlecheng
上面的還不是源碼嗎 支援(0) 反對(0) #7樓 2013-06-12 21:33 | draem0507
@nickycookie
引用 publicProgress好像應該是publishProgress
好眼力 支援(0) 反對(0) #8樓 2013-11-21 15:49 | youJumpILook
我見過的最适合新手入門的例子 支援(0) 反對(0) #9樓 2014-02-28 10:56 | 十旋轉45度
謝謝分享,講解簡單明了 支援(0) 反對(0) #10樓 2014-03-06 16:40 | 沉默年代
簡單明了,好好好 支援(0) 反對(0) #11樓 2014-04-14 16:00 | jiangyi355
樓主,如果沒有參數要帶入呢 支援(0) 反對(0) #12樓 2014-06-17 11:38 | shashashashi
很好的講解。 支援(0) 反對(0) #13樓 2014-12-08 23:42 | 長風無及
good article! 支援(0) 反對(0) #14樓 2014-12-15 21:56 | 想飛的更遠
你好,部落客,你是怎麼知道進度是
int vlaue = values[0];
progressBar.setProgress(vlaue);
的呢?這個values[0]是什麼意義呢? 支援(0) 反對(0) #15樓 2015-02-09 10:23 | 修雨軒陳
@renfujiang
在onProgressUpdate()裡面的progressBar.setProgress(values[0]);, doInBackground()中調用了這個方法 , 支援(0) 反對(0) #16樓 2015-02-09 10:24 | 修雨軒陳
@想飛的更遠
引用 你好,部落客,你是怎麼知道進度是
int vlaue = values[0];
progressBar.setProgress(vlaue);
的呢?這個values[0]是什麼意義呢?
Integer ...value是不能确定數量的參數 , java的main(String[] args)也是ruc , 是以第一個參數就是 下标為0的參數 支援(0) 反對(0) #17樓 2015-03-17 15:23 | 生來笨鳥
@想飛的更遠
通過doInBackground(Integer... params) 裡調用的publishProgress(Integer... params)方法,把進度發送到onProgressUpdate()裡 支援(0) 反對(0) #18樓 2015-09-17 16:46 | 聞香識好
怒關注一記。 支援(0) 反對(0) #19樓 2015-11-13 14:50 | 林姐姐
感謝部落客~ 支援(0) 反對(0) #20樓 2016-01-04 10:33 | 冬眠FF
謝謝部落客! 支援(0) 反對(0) #21樓 2016-02-19 15:41 | 蕭穎儇
寫的很好~适合入門學習~多謝部落客共享 支援(0) 反對(0) #22樓 2016-03-08 19:36 | zzqhfuf
簡單明了,樓主好樣的
詳解Android中AsyncTask的使用
标簽: android任務layoutbuttonthreadasynchronous 2011-06-08 19:00 182398人閱讀 評論(116) 收藏 舉報
分類: Android(31)
版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。
在Android中實作異步任務機制有兩種方式,Handler和AsyncTask。
Handler模式需要為每一個任務建立一個新的線程,任務完成後通過Handler執行個體向UI線程發送消息,完成界面的更新,這種方式對于整個過程的控制比較精細,但也是有缺點的,例如代碼相對臃腫,在多個任務同時執行時,不易對線程進行精确的控制。關于Handler的相關知識,前面也有所介紹,不清楚的朋友們可以參照一下。
為了簡化操作,Android1.5提供了工具類android.os.AsyncTask,它使建立異步任務變得更加簡單,不再需要編寫任務線程和Handler執行個體即可完成相同的任務。
先來看看AsyncTask的定義:
[java] view plain copy
- 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的代碼:
[java] view plain copy
- package com.scott.async;
- import java.io.ByteArrayOutputStream;
- import java.io.InputStream;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.HttpStatus;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.impl.client.DefaultHttpClient;
- import android.app.Activity;
- import android.os.AsyncTask;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.widget.Button;
- import android.widget.ProgressBar;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- private static final String TAG = "ASYNC_TASK";
- private Button execute;
- private Button cancel;
- private ProgressBar progressBar;
- private TextView textView;
- private MyTask mTask;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- execute = (Button) findViewById(R.id.execute);
- execute.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //注意每次需new一個執行個體,建立的任務隻能執行一次,否則會出現異常
- mTask = new MyTask();
- mTask.execute("http://www.baidu.com");
- execute.setEnabled(false);
- cancel.setEnabled(true);
- }
- });
- cancel = (Button) findViewById(R.id.cancel);
- cancel.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- //取消一個正在執行的任務,onCancelled方法将會被調用
- mTask.cancel(true);
- }
- });
- progressBar = (ProgressBar) findViewById(R.id.progress_bar);
- textView = (TextView) findViewById(R.id.text_view);
- }
- private class MyTask extends AsyncTask<String, Integer, String> {
- //onPreExecute方法用于在執行背景任務前做一些UI操作
- @Override
- protected void onPreExecute() {
- Log.i(TAG, "onPreExecute() called");
- textView.setText("loading...");
- }
- //doInBackground方法内部執行背景任務,不可在此方法内修改UI
- @Override
- protected String doInBackground(String... params) {
- Log.i(TAG, "doInBackground(Params... params) called");
- try {
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(params[0]);
- HttpResponse response = client.execute(get);
- if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
- HttpEntity entity = response.getEntity();
- InputStream is = entity.getContent();
- long total = entity.getContentLength();
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buf = new byte[1024];
- int count = 0;
- int length = -1;
- while ((length = is.read(buf)) != -1) {
- baos.write(buf, 0, length);
- count += length;
- //調用publishProgress公布進度,最後onProgressUpdate方法将被執行
- publishProgress((int) ((count / (float) total) * 100));
- //為了示範進度,休眠500毫秒
- Thread.sleep(500);
- }
- return new String(baos.toByteArray(), "gb2312");
- }
- } catch (Exception e) {
- Log.e(TAG, e.getMessage());
- }
- return null;
- }
- //onProgressUpdate方法用于更新進度資訊
- @Override
- protected void onProgressUpdate(Integer... progresses) {
- Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
- progressBar.setProgress(progresses[0]);
- textView.setText("loading..." + progresses[0] + "%");
- }
- //onPostExecute方法用于在執行完背景任務後更新UI,顯示結果
- @Override
- protected void onPostExecute(String result) {
- Log.i(TAG, "onPostExecute(Result result) called");
- textView.setText(result);
- execute.setEnabled(true);
- cancel.setEnabled(false);
- }
- //onCancelled方法用于在取消執行中的任務時更改UI
- @Override
- protected void onCancelled() {
- Log.i(TAG, "onCancelled() called");
- textView.setText("cancelled");
- progressBar.setProgress(0);
- execute.setEnabled(true);
- cancel.setEnabled(false);
- }
- }
- }
布局檔案main.xml代碼如下:
[xhtml] view plain copy
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
- <Button
- android:id="@+id/execute"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="execute"/>
- <Button
- android:id="@+id/cancel"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:enabled="false"
- android:text="cancel"/>
- <ProgressBar
- android:id="@+id/progress_bar"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:progress="0"
- android:max="100"
- style="?android:attr/progressBarStyleHorizontal"/>
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <TextView
- android:id="@+id/text_view"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"/>
- </ScrollView>
- </LinearLayout>
因為需要通路網絡,是以我們還需要在AndroidManifest.xml中加入通路網絡的權限:
[xhtml] view plain copy
- <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枚舉類代碼段如下:
[java] view plain copy
- //初始狀态
- private volatile Status mStatus = Status.PENDING;
- public enum Status {
- PENDING,
- RUNNING,
- FINISHED,
- }
- public final Status getStatus() {
- return mStatus;
- }
可以看到,AsyncTask的初始狀态為PENDING,代表待定狀态,RUNNING代表執行狀态,FINISHED代表結束狀态,這幾種狀态在AsyncTask一次生命周期内的很多地方被使用,非常重要。
介紹完大綱視圖相關内容之後,接下來,我們會從execute(Params... params)作為入口,重點分析一下AsyncTask的執行流程,我們來看一下execute(Params... params)方法的代碼段:
[java] view plain copy
- public final AsyncTask<Params, Progress, Result> execute(Params... params) {
- if (mStatus != Status.PENDING) {
- switch (mStatus) {
- case RUNNING:
- //如果該任務正在被執行則抛出異常
- //值得一提的是,在調用cancel取消任務後,狀态仍未RUNNING
- throw new IllegalStateException("Cannot execute task:"
- + " the task is already running.");
- case FINISHED:
- //如果該任務已經執行完成則抛出異常
- throw new IllegalStateException("Cannot execute task:"
- + " the task has already been executed "
- + "(a task can be executed only once)");
- }
- }
- //改變狀态為RUNNING
- mStatus = Status.RUNNING;
- //調用onPreExecute方法
- onPreExecute();
- mWorker.mParams = params;
- sExecutor.execute(mFuture);
- return this;
- }
代碼中涉及到三個陌生的變量:mWorker、sExecutor、mFuture,我們也會看一下他們的廬山真面目:
關于sExecutor,它是java.util.concurrent.ThreadPoolExecutor的執行個體,用于管理線程的執行。代碼如下:
[java] view plain copy
- private static final int CORE_POOL_SIZE = 5;
- private static final int MAXIMUM_POOL_SIZE = 128;
- private static final int KEEP_ALIVE = 10;
- //建立一個隊列用來存放線程
- private static final BlockingQueue<Runnable> sWorkQueue =
- new LinkedBlockingQueue<Runnable>(10);
- //建立一個線程工廠
- private static final ThreadFactory sThreadFactory = new ThreadFactory() {
- private final AtomicInteger mCount = new AtomicInteger(1);
- //建立一個線程
- public Thread newThread(Runnable r) {
- return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
- }
- };
- //建立一個線程池執行器,用于管理線程的執行
- private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
- MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
mWorker實際上是AsyncTask的一個的抽象内部類的實作對象執行個體,它實作了Callable<Result>接口中的call()方法,代碼如下:
[java] view plain copy
- private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
- Params[] mParams;
- }
而mFuture實際上是java.util.concurrent.FutureTask的執行個體,下面是它的FutureTask類的相關資訊:
[java] view plain copy
- public class FutureTask<V> implements RunnableFuture<V> {
[java] view plain copy
- public interface RunnableFuture<V> extends Runnable, Future<V> {
- void run();
- }
可以看到FutureTask是一個可以中途取消的用于異步計算的類。
下面是mWorker和mFuture執行個體在AsyncTask中的展現:
[java] view plain copy
- private final WorkerRunnable<Params, Result> mWorker;
- private final FutureTask<Result> mFuture;
- public AsyncTask() {
- mWorker = new WorkerRunnable<Params, Result>() {
- //call方法被調用後,将設定優先級為背景級别,然後調用AsyncTask的doInBackground方法
- public Result call() throws Exception {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- return doInBackground(mParams);
- }
- };
- //在mFuture執行個體中,将會調用mWorker做背景任務,完成後會調用done方法
- mFuture = new FutureTask<Result>(mWorker) {
- @Override
- protected void done() {
- Message message;
- Result result = null;
- try {
- result = get();
- } catch (InterruptedException e) {
- android.util.Log.w(LOG_TAG, e);
- } catch (ExecutionException e) {
- throw new RuntimeException("An error occured while executing doInBackground()",
- e.getCause());
- } catch (CancellationException e) {
- //發送取消任務的消息
- message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
- new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
- message.sendToTarget();
- return;
- } catch (Throwable t) {
- throw new RuntimeException("An error occured while executing "
- + "doInBackground()", t);
- }
- //發送顯示結果的消息
- message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(AsyncTask.this, result));
- message.sendToTarget();
- }
- };
- }
我們看到上面的代碼中,mFuture執行個體對象的done()方法中,如果捕捉到了CancellationException類型的異常,則發送一條“MESSAGE_POST_CANCEL”的消息;如果順利執行,則發送一條“MESSAGE_POST_RESULT”的消息,而消息都與一個sHandler對象關聯。這個sHandler執行個體實際上是AsyncTask内部類InternalHandler的執行個體,而InternalHandler正是繼承了Handler,下面我們來分析一下它的代碼:
[java] view plain copy
- private static final int MESSAGE_POST_RESULT = 0x1; //顯示結果
- private static final int MESSAGE_POST_PROGRESS = 0x2; //更新進度
- private static final int MESSAGE_POST_CANCEL = 0x3; //取消任務
- private static final InternalHandler sHandler = new InternalHandler();
- private static class InternalHandler extends Handler {
- @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
- @Override
- public void handleMessage(Message msg) {
- AsyncTaskResult result = (AsyncTaskResult) msg.obj;
- switch (msg.what) {
- case MESSAGE_POST_RESULT:
- // There is only one result
- //調用AsyncTask.finish方法
- result.mTask.finish(result.mData[0]);
- break;
- case MESSAGE_POST_PROGRESS:
- //調用AsyncTask.onProgressUpdate方法
- result.mTask.onProgressUpdate(result.mData);
- break;
- case MESSAGE_POST_CANCEL:
- //調用AsyncTask.onCancelled方法
- result.mTask.onCancelled();
- break;
- }
- }
- }
我們看到,在處理消息時,遇到“MESSAGE_POST_RESULT”時,它會調用AsyncTask中的finish()方法,我們來看一下finish()方法的定義:
[java] view plain copy
- private void finish(Result result) {
- if (isCancelled()) result = null;
- onPostExecute(result); //調用onPostExecute顯示結果
- mStatus = Status.FINISHED; //改變狀态為FINISHED
- }
原來finish()方法是負責調用onPostExecute(Result result)方法顯示結果并改變任務狀态的啊。
另外,在mFuture對象的done()方法裡,建構一個消息時,這個消息包含了一個AsyncTaskResult類型的對象,然後在sHandler執行個體對象的handleMessage(Message msg)方法裡,使用下面這種方式取得消息中附帶的對象:
[java] view plain copy
- AsyncTaskResult result = (AsyncTaskResult) msg.obj;
這個AsyncTaskResult究竟是什麼呢,它又包含什麼内容呢?其實它也是AsyncTask的一個内部類,是用來包裝執行結果的一個類,讓我們來看一下它的代碼結構:
[java] view plain copy
- @SuppressWarnings({"RawUseOfParameterizedType"})
- private static class AsyncTaskResult<Data> {
- final AsyncTask mTask;
- final Data[] mData;
- AsyncTaskResult(AsyncTask task, Data... data) {
- mTask = task;
- mData = data;
- }
- }
看以看到這個AsyncTaskResult封裝了一個AsyncTask的執行個體和某種類型的資料集,我們再來看一下建構消息時的代碼:
[java] view plain copy
- //發送取消任務的消息
- message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
- new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
- message.sendToTarget();
[java] view plain copy
- //發送顯示結果的消息
- message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
- new AsyncTaskResult<Result>(AsyncTask.this, result));
- message.sendToTarget();
在處理消息時是如何使用這個對象呢,我們再來看一下:
[java] view plain copy
- result.mTask.finish(result.mData[0]);
[java] view plain copy
- 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的良好封裝,減少了開發者處理問題的複雜度,提高了開發效率,希望朋友們能多多體會一下。
- 頂
- 25
- 踩
- 上一篇詳解Android首選項架構的使用
- 下一篇淺談WebView的使用
[Android] AsyncTask使用執行個體---加載網絡圖檔
标簽: android網絡floatstringnullui 2012-07-12 18:46 5445人閱讀 評論(4) 收藏 舉報
分類: Android(47)
版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。
先上效果圖。如demo_asynctask.gif
對于圖檔的加載效果,見連結:[Android] PorterDuff使用執行個體----實作新浪微網誌圖檔下載下傳效果
本文參考連結:http://developer.android.com/reference/android/os/AsyncTask.html
AsyncTask被設計成友善編寫Thread與Handler互動的輔助類,幾秒鐘的背景耗時操作是理想的使用場合。
AsyncTask必須被子類化才能使用,在該過程中必須設定3個構造參數:
1.Params 往背景線程執行時的參數類型
2.Progress 背景線程釋出任務進度的參數類型
3.Result 背景線程的運作後最後傳回的結果的參數類型
如果不需要聲明參數類型,可以使用"Void"進行替代。
通常子類化聲明代碼:
[java] view plain copy
- public class DownloadImgTask extends AsyncTask<String, Float, Bitmap>
不需要參數的聲明代碼:
[java] view plain copy
- 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.
本文為Sodino所有,轉載請注明出處:http://blog.csdn.net/sodino/article/details/7741674
Java代碼貼上,XML請各位看官自行實作:
[java] view plain copy
- ActAsyncTask.java
- package lab.sodino.asynctask;
- import android.app.Activity;
- import android.os.Bundle;
- import android.view.View;
- import android.view.View.OnClickListener;
- public class ActAsyncTask extends Activity implements OnClickListener {
- private PorterDuffView pViewA, pViewB, pViewC, pViewD;
- public static final String[] STRING_ARR = {//
- "http://developer.android.com/images/home/android-jellybean.png",//
- "http://developer.android.com/images/home/design.png",//
- "http://developer.android.com/images/home/google-play.png",//
- "http://developer.android.com/images/home/google-io.png" };
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- pViewA = (PorterDuffView) findViewById(R.id.pViewA);
- pViewA.setOnClickListener(this);
- pViewB = (PorterDuffView) findViewById(R.id.pViewB);
- pViewB.setOnClickListener(this);
- pViewC = (PorterDuffView) findViewById(R.id.pViewC);
- pViewC.setOnClickListener(this);
- pViewD = (PorterDuffView) findViewById(R.id.pViewD);
- pViewD.setOnClickListener(this);
- }
- public void onClick(View v) {
- if (v instanceof PorterDuffView) {
- PorterDuffView pdView = (PorterDuffView) v;
- if (pdView.isLoading() == false) {
- DownloadImgTask task = new DownloadImgTask(pdView);
- task.execute(STRING_ARR[pdView.getId() % STRING_ARR.length]);
- pdView.setPorterDuffMode(true);
- pdView.setLoading(true);
- pdView.setProgress(0);
- pdView.invalidate();
- }
- }
- }
- }
[java] view plain copy
- DownloadImgTask.java
- package lab.sodino.asynctask;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import org.apache.http.Header;
- import org.apache.http.HttpEntity;
- import org.apache.http.HttpResponse;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.HttpClient;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.impl.client.DefaultHttpClient;
- import org.apache.http.params.HttpParams;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.os.AsyncTask;
- public class DownloadImgTask extends AsyncTask<String, Float, Bitmap> {
- private PorterDuffView pdView;
- public DownloadImgTask(PorterDuffView pdView) {
- this.pdView = pdView;
- }
- protected void onPreExecute() {
- LogOut.out(this, "onPreExecute");
- }
- protected Bitmap doInBackground(String... params) {
- LogOut.out(this, "doInBackground:" + params[0]);
- HttpClient httpClient = new DefaultHttpClient();
- HttpGet httpGet = new HttpGet(params[0]);
- InputStream is = null;
- ByteArrayOutputStream baos = null;
- try {
- HttpResponse httpResponse = httpClient.execute(httpGet);
- printHttpResponse(httpResponse);
- HttpEntity httpEntity = httpResponse.getEntity();
- long length = httpEntity.getContentLength();
- LogOut.out(this, "content length=" + length);
- is = httpEntity.getContent();
- if (is != null) {
- baos = new ByteArrayOutputStream();
- byte[] buf = new byte[128];
- int read = -1;
- int count = 0;
- while ((read = is.read(buf)) != -1) {
- baos.write(buf, 0, read);
- count += read;
- publishProgress(count * 1.0f / length);
- }
- LogOut.out(this, "count=" + count + " length=" + length);
- byte[] data = baos.toByteArray();
- Bitmap bit = BitmapFactory.decodeByteArray(data, 0, data.length);
- return bit;
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (baos != null) {
- baos.close();
- }
- if (is != null) {
- is.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return null;
- }
- protected void onProgressUpdate(Float... progress) {
- // LogOut.out(this, "onProgressUpdate");
- pdView.setProgress(progress[0]);
- }
- protected void onPostExecute(Bitmap bit) {
- LogOut.out(this, "onPostExecute");
- pdView.setPorterDuffMode(false);
- pdView.setLoading(false);
- pdView.setImageBitmap(bit);
- }
- protected void onCancelled() {
- LogOut.out(this, "DownloadImgTask cancel...");
- super.onCancelled();
- }
- private void printHttpResponse(HttpResponse httpResponse) {
- Header[] headerArr = httpResponse.getAllHeaders();
- for (int i = 0; i < headerArr.length; i++) {
- Header header = headerArr[i];
- LogOut.out(this, "name[" + header.getName() + "]value[" + header.getValue() + "]");
- }
- HttpParams params = httpResponse.getParams();
- LogOut.out(this, String.valueOf(params));
- LogOut.out(this, String.valueOf(httpResponse.getLocale()));
- }
- }
[java] view plain copy
- PorterDuffView.java
- package lab.sodino.asynctask;
- import java.text.DecimalFormat;
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.PorterDuff;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.widget.ImageView;
- public class PorterDuffView extends ImageView {
- public static final int FG_HEIGHT = 1;
- // public static final int FOREGROUND_COLOR = 0x77123456;
- public static final int FOREGROUND_COLOR = 0x77ff0000;
- public static final int TEXT_COLOR = 0xff7fff00;
- public static final int FONT_SIZE = 30;
- private Bitmap bitmapBg, bitmapFg;
- private Paint paint;
- private float progress;
- private int width, height;
- private DecimalFormat decFormat;
- private float txtBaseY;
- private boolean porterduffMode;
- private boolean loading;
- public PorterDuffView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
- private static Bitmap createForegroundBitmap(int w) {
- Bitmap bm = Bitmap.createBitmap(w, FG_HEIGHT, Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(bm);
- Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- p.setColor(FOREGROUND_COLOR);
- c.drawRect(0, 0, w, FG_HEIGHT, p);
- return bm;
- }
- private void init(Context context, AttributeSet attrs) {
- if (attrs != null) {
- // //
- // int count = attrs.getAttributeCount();
- // for (int i = 0; i < count; i++) {
- // LogOut.out(this, "attrNameRes:" +
- // Integer.toHexString(attrs.getAttributeNameResource(i))//
- // + " attrName:" + attrs.getAttributeName(i)//
- // + " attrResValue:" + attrs.getAttributeResourceValue(i, -1)//
- // + " attrValue:" + attrs.getAttributeValue(i)//
- // );
- // }
- // //
- TypedArray typedArr = context.obtainStyledAttributes(attrs, R.styleable.porterduff_PorterDuffView);
- porterduffMode = typedArr.getBoolean(R.styleable.porterduff_PorterDuffView_porterduffMode, false);
- }
- Drawable drawable = getDrawable();
- if (porterduffMode && drawable != null && drawable instanceof BitmapDrawable) {
- bitmapBg = ((BitmapDrawable) drawable).getBitmap();
- width = bitmapBg.getWidth();
- height = bitmapBg.getHeight();
- // LogOut.out(this, "width=" + width + " height=" + height);
- bitmapFg = createForegroundBitmap(width);
- } else {
- // 不符合要求,自動設定為false。
- porterduffMode = false;
- }
- paint = new Paint();
- paint.setFilterBitmap(false);
- paint.setAntiAlias(true);
- paint.setTextSize(FONT_SIZE);
- // 關于FontMetrics的詳情介紹,可見:
- // http://xxxxxfsadf.iteye.com/blog/480454
- Paint.FontMetrics fontMetrics = paint.getFontMetrics();
- // 注意觀察本輸出:
- // ascent:單個字元基線以上的推薦間距,為負數
- LogOut.out(this, "ascent:" + fontMetrics.ascent//
- // descent:單個字元基線以下的推薦間距,為正數
- + " descent:" + fontMetrics.descent //
- // 單個字元基線以上的最大間距,為負數
- + " top:" + fontMetrics.top //
- // 單個字元基線以下的最大間距,為正數
- + " bottom:" + fontMetrics.bottom//
- // 文本行與行之間的推薦間距
- + " leading:" + fontMetrics.leading);
- // 在此處直接計算出來,避免了在onDraw()處的重複計算
- txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;
- decFormat = new DecimalFormat("0.0%");
- }
- public void onDraw(Canvas canvas) {
- if (porterduffMode) {
- int tmpW = (getWidth() - width) / 2, tmpH = (getHeight() - height) / 2;
- // 畫出背景圖
- canvas.drawBitmap(bitmapBg, tmpW, tmpH, paint);
- // 設定PorterDuff模式
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
- // canvas.drawBitmap(bitmapFg, tmpW, tmpH - progress * height,
- // paint);
- int tH = height - (int) (progress * height);
- for (int i = 0; i < tH; i++) {
- canvas.drawBitmap(bitmapFg, tmpW, tmpH + i, paint);
- }
- // 立即取消xfermode
- paint.setXfermode(null);
- int oriColor = paint.getColor();
- paint.setColor(TEXT_COLOR);
- paint.setTextSize(FONT_SIZE);
- String tmp = decFormat.format(progress);
- float tmpWidth = paint.measureText(tmp);
- canvas.drawText(decFormat.format(progress), tmpW + (width - tmpWidth) / 2, tmpH + txtBaseY, paint);
- // 恢複為初始值時的顔色
- paint.setColor(oriColor);
- } else {
- LogOut.out(this, "onDraw super");
- super.onDraw(canvas);
- }
- }
- public void setProgress(float progress) {
- if (porterduffMode) {
- this.progress = progress;
- // 重新整理自身。
- invalidate();
- }
- }
- public void setBitmap(Bitmap bg) {
- if (porterduffMode) {
- bitmapBg = bg;
- width = bitmapBg.getWidth();
- height = bitmapBg.getHeight();
- bitmapFg = createForegroundBitmap(width);
- Paint.FontMetrics fontMetrics = paint.getFontMetrics();
- txtBaseY = (height - fontMetrics.bottom - fontMetrics.top) / 2;
- setImageBitmap(bg);
- // 請求重新布局,将會再次調用onMeasure()
- // requestLayout();
- }
- }
- public boolean isLoading() {
- return loading;
- }
- public void setLoading(boolean loading) {
- this.loading = loading;
- }
- public void setPorterDuffMode(boolean bool) {
- porterduffMode = bool;
- }
- }
- 頂
- 2
- 踩
- 上一篇[Android] PorterDuff使用執行個體----實作新浪微網誌圖檔下載下傳效果
- 下一篇[Java] 生産者&消費者問題