public abstract class AsyncTask< Params, Progress, Result > 對於android中的處理異步線程的機制, 能夠更加簡單的處理異步任務. 如下圖是該抽象類的大綱
其中,doInBackground(Params… params)是一個抽象方法,我們繼承AsyncTask時必須覆寫此方法;onPreExecute()、onProgressUpdate(Progress… values)、onPostExecute(Result result)、onCancelled()這幾個方法體都是空的,我們需要的時候可以選擇性的覆寫它們;execute((Params… params)是觸發一個實例task任務時調用的;publishProgress(Progress… values)是final修飾的,不能覆寫,只能去調用,我們一般會在doInBackground(Params… params)中調用此方法;另外,我們可以看到有一個Status的枚舉類和getStatus()方法,Status枚舉類中記錄了該AsyncTask任務的三種狀態, AsyncTask的初始狀態爲PENDING,代表待定狀態,RUNNING代表執行狀態,FINISHED代表結束狀態,我們可以通過這幾種狀態,對AsyncTask進行啓動,終止等操作。
第二部分 介紹其基本使用
以打開百度首頁爲例子,介紹task的使用步驟:
第一步: 繼承抽象類AsyncTask構建子類,必須覆寫doInBackground(String… params) 方法,其他方法根據需要調用或者覆寫,在doInBackground(String… params)方法中,觸發異步請求,也就是請求 http://www.baidu.com , 並通過覆寫onPreExecute(), 將異步請求執行之前的UI界面進行處理.
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);
}
//onCancelled方法用於在取消執行中的任務時更改UI
@Override
protected void onCancelled() {
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0);
}
}
}
第二步:在UI線程中創建task實例 並觸發
mTask = new MyTask();
mTask.execute(“http://www.baidu.com“);
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);
cancel = (Button) findViewById(R.id.cancel);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view);
execute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注意每次需new一個實例,新建的任務只能執行一次,否則會出現異常
mTask = new MyTask();
mTask.execute("http://www.baidu.com");
}
});
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一個正在執行的任務,onCancelled方法將會被調用 ,該方法只是將對應的task標記爲cancel狀態 並不是真正的取消線程的執行,線程在後臺仍然繼續執行 另 java中不能直接見到粗暴的結束某一個線程
mTask.cancel(true);
}
});
}
第三步: 與UI通信 通過onPostExecute(String result) 方法將異步請求的結果傳遞給UI線程,更新UI界面.
//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);
}
以該例子對該抽象類public abstract class AsyncTask< Params, Progress, Result > 的三種泛型參數做如下介紹:
Params 是“啓動任務執行的輸入參數” 觸發異步任務執行的時候會調用execute(Params… params),去執行一個異步任務, 該方法將參數傳遞給後臺真正異步線程,onPreExecute(),在execute(Params… params)被調用後立即執行,一般用來在執行後臺任務前對UI做一些標記。
Progress 是“後臺任務執行的進度”,在進行doInBackground的過程中,可以通過調用publishProgress(Progress… values)將異步執行的進度告訴UI線程,UI線程通過onProgressUpdate(Progress… values)將後臺執行的進度公佈在UI界面中
Result 是”後臺計算結果的類型”當doInBackground執行完畢或者出現異常時,會通過onPostExecute(Result result)方法對加載的結果傳遞給UI線程,並更新UI界面.
例子中傳入的url = “http://www.baidu.com“是String類型的,並使用(int) ((count / (float) total) * 100)來表示加載的進度,所以是Integer類型,最後將獲取的結果以new String(baos.toByteArray(), “gb2312”)類型傳遞給UI界面的,
因此例子中的MyTask是以String爲請求參數 以Integer爲進度值,以String爲結果的異步任務去執行的. 所以創建實力的時候是 private class MyTask extends AsyncTask< String, Integer, String >
在特定場合下,並不是所有類型都被使用,如果沒有被使用,可以用java.lang.Void類型代替。
總結:
一個異步任務的執行一般包括以下幾個步驟:
1.execute(Params… params),在主線程通過調用task的該方法執行一個異步任務
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)這幾個方法。都是系統調用的,不能手動調用,只要執行execute即可觸發一個異步任務的執行.
4.不能在doInBackground(Params… params)中更改UI組件的信息, 該方法實在其他線程中執行的,onPreExecute(),onProgressUpdate(Progress… values),onPostExecute(Result result)這幾個方法承接了異步線程處理的結果,都是運行在UI線程中,可直接更新UI界面.
5.一個任務實例只能執行一次,如果執行第二次將會拋出RuntimeException異常,也就是例子中mTask.execute(“http://www.baidu.com“) 只能執行一次.
第三部分 介紹其工作原理
從第二部分可知,一個異步任務的觸發是由execute開始的,從這裏作爲入口看看AsyncTask真正的工作流程
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;
}
從execute的代碼段中,可知,對Status.PENDING狀態的task在會首先調用onPreExecute()方法,來標記UI界面的狀態,之後將params傳遞給mWorker,並執行sExecutor.execute(mFuture);
其中 sExecutor是java.util.concurrent.ThreadPoolExecutor的實例,用於管理線程的執行, 也就是在線程池中創建一個新的線程,用於執行後臺任務.
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的實例,它實現了Callable< Result >接口中的call()方法,
mFuture實際上是java.util.concurrent.FutureTask的實例,下面是它的FutureTask類的相關信息:
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
所以從代碼中可以看到 其實mFuture是Runnable的子類
mWorker和mFuture在AsyncTask中的體現如下, 在執行onPreExecute()方法,標記了主線程之後,將參數傳遞給mWorker,再通過ThreadPoolExecutor的實例sExecutor觸發了以mWorker爲參數的mFuture方法,mFuture實例中會調用mWorker做後臺任務,而覆寫call方法的mWorker實例是將該任務設置爲後臺級別,並調用doInBackground方法開始執行後臺任務,後臺任務完成後系統會調用done方法,將後臺結果發送出去.
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()方法中,通過不通的情況會發送不同的由AsyncTaskResult< Result>()構建的消息,將不同的結果發送出去.如果捕捉到了CancellationException類型的異常,則發送一條“MESSAGE_POST_CANCEL”的消息;如果順利執行,則發送一條“MESSAGE_POST_RESULT”的消息,而消息都與一個sHandler對象關聯。
這個sHandler實例實際上是AsyncTask內部類InternalHandler的實例,而InternalHandler正是繼承了Handler
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;
}
}
}
由sHandler的代碼可知,在sHandler實例對象的handleMessage(Message msg)方法裏,使用AsyncTaskResult result = (AsyncTaskResult) msg.obj方式取得消息中附帶的AsyncTaskResult消息
而AsyncTaskResult其實也是AsyncTask的一個內部類,是用來包裝執行結果的一個類,讓我們來看一下它的代碼結構:
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可知這個AsyncTaskResult封裝了一個AsyncTask的實例和某種類型的數據集.所以在發送消息時是將AsyncTask實例和後臺結果result發送出去.
sHandler在處理消息時是如何使用這個task對象和數據集呢? 舉例如下
result.mTask.finish(result.mData[0]);
result.mTask.onProgressUpdate(result.mData);
分析一:當InternalHandler類型的實例sHandler收到MESSAGE_POST_RESULT類型的AsyncTaskResult消息時,是通過傳進來的AsyncTask.this來調用它自己的finish方法,代碼如下,從finish方法中可以看到,其實是調動AsyncTask的onPostExecute(result)方法 顯示結果.
private void finish(Result result) {
if (isCancelled()) result = null;
onPostExecute(result); //調用onPostExecute顯示結果
mStatus = Status.FINISHED; //改變狀態爲FINISHED
}
總結
在調用execute(Params… params)方法後,execute方法會調用onPreExecute()方法,然後由ThreadPoolExecutor實例sExecutor執行一個帶WorkerRunnable參數的FutureTask任務,這個過程中doInBackground(Params… params)將被調用,當後臺任務結束以後,會調用FutureTask的done方法,將後臺結果通過InternalHandler實例sHandler發送出去.發送的消息由AsyncTaskResult構建,包含了該異步實例task和後臺任務的結果result. 對不同類型的消息,使用task實例調用不同的方法來完成與UI線程的通信.
1 MESSAGE_POST_PROGRESS消息:result.mTask.onProgressUpdate(result.mData);
如果被開發者覆寫的doInBackground(Params… params)方法中調用了publishProgress(Progress… values)方法,則通過InternalHandler實例sHandler發送一條MESSAGE_POST_PROGRESS消息,更新進度,sHandler處理消息時onProgressUpdate(Progress… values)方法將被調用;
2 MESSAGE_POST_CANCEL消息:result.mTask.onCancelled();
如果遇到異常,則發送一條MESSAGE_POST_CANCEL的消息,取消任務,sHandler處理消息時onCancelled()方法將被調用;
3 MESSAGE_POST_RESULT消息:result.mTask.finish(result.mData[0]);
如果執行成功,則發送一條MESSAGE_POST_RESULT的消息,顯示結果,sHandler處理消息時onPostExecute(Result result)方法被調用。
可知AsyncTask的本質就是對Thread+Handler的良好封裝,(sExecutor,mFuture,mWorker 均屬於Thread類,sHandler屬於Handler類)減少了開發者處理問題的複雜度,提高了開發效率。