Android的AsyncTask比Handler更輕量級一些,是用來做簡單的異步處理的。
使用的優點:
l 簡單,快捷
l 過程可控
使用的缺點:
l 在使用多個異步操作和並需要進行Ui變更時,就變得複雜起來。
AsyncTask定義了三種泛型類型 Params,Progress和Result。(也是可以指定爲空的,如 AsyncTask <Void, Void, Void>)
- Params 啓動任務執行的輸入參數。
- Progress 後臺任務執行的百分比。
- Result 後臺執行任務最終返回的結果。
一個異步加載數據最少要重寫以下這兩個方法:
- doInBackground(Params…) 後臺執行,比較耗時的操作都可以放在這裏。此方法在後臺線程執行,完成任務的主要工作。在執行過程中可以調用publishProgress(Progress…)來更新任務的進度。其參數對應AsyncTask的第一個參數,publishProgress(Progress…)的參數對應AsyncTask的第二個參數,其返回值對應AsyncTask的第三個參數。
- onPostExecute(Result) 相當於Handler 處理UI的方式,在這裏面可以使用在doInBackground 得到的返回結果操作UI。 此方法在主線程執行,任務執行的結果(doInBackground的返回值)作爲此方法的參數。
當然了,還可以重寫以下這三個方法,但不是必須的:
- onProgressUpdate(Progress…) 可以使用進度條增加用戶體驗度。 此方法在主線程執行,用於顯示任務執行的進度。
- onPreExecute() 調用excute()方法時執行,當任務執行之前開始調用此方法。
- onCancelled() 用戶調用取消(調用了cancel(true))時,要做的操作。
使用AsyncTask類,以下是幾條必須遵守的準則:
1、Task的實例必須在UI thread中創建;
2、execute方法必須在UI thread中調用;
3、不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;
4、該task只能被執行一次,多次調用時將會出現異常;
執行的順序是:onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()。onCancelled()不一定會執行。
除了doInBackground是在子線程執行,其他的都是在UI線程執行的。
下面說下我們具體是怎麼使用的:
第一步,創建一個異步任務的實例,並且執行,這裏傳入的參數對應的是AsyncTask的第一個參數:
MyTask mTask = new MyTask();
mTask.execute(url);
第二步,建立一個異步任務的類,如下:
private class MyTask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用於在執行後臺任務前做一些UI操作
@Override
protected void onPreExecute() {
textView.setText("start loading...");
}
//doInBackground方法內部執行後臺任務,不可在此方法內修改UI。這裏傳入的參數對應AsyncTask的第一個參數,即execute()函數傳遞過來的
@Override
protected String doInBackground(String... params) {
... //做一些耗時的動作
publishProgress(progresses); //progresses對應AsyncTask的第二個參數,同時也是要傳給onProgressUpdate()的參數
...
return result; //返回值對應AsyncTask的第三個參數,同時也是要傳給onPostExecute()的參數
}
//onProgressUpdate方法用於更新進度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用於在執行完後臺任務後更新UI,顯示結果
@Override
protected void onPostExecute(String result) {
textView.setText(result);
}
//onCancelled方法用於在取消執行中的任務時更改UI
@Override
protected void onCancelled() {
textView.setText("cancelled");
}
}
第三步,不是必須的,如果需要取消任務,可以調用:mTask.cancle(true);
該函數一旦被調用就不會執行到onPostExecute()了,而是執行onCancelled()方法。這麼簡單強大的功能是怎麼實現的呢?我們跟着源碼來一探究竟。
先看下AsyncTask的構建函數:
我們要啓動某一任務都會執行下execute()方法,看下它的源碼,如下:
mActivie爲null的意思是當前沒有任務在執行,如果mActivie!=null,那麼說明當前有任務正在執行,那麼只要把任務添加到mTasks裏面即可。
因爲任務執行完畢後,會再次調用scheduleNext()方法的,就是
finally {
scheduleNext();
}
這樣就形成了一種鏈狀調用結構,只要mTasks裏面還有任務,就會不斷逐一調用,如果後面有任務進來,就只要添加到mTasks裏面即可。
這裏給execute()傳遞的參數是mFuture,所以會執行到mFuture的run()方法,而run()方法最終會調用如下方法:
那MESSAGE_POST_PROGRESS消息是什麼時候發送的呢?猜想一下,肯定是在publishProgress()裏面,看下實現:
使用多線程實現的話,有一個很大的缺陷,那就是線程池總大小是128,也就是如果任務數量超過了128,那麼程序就會崩潰,如果有興趣可以看我下面一篇文章《Android AsyncTask兩種線程池分析和總結 》。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
}
就是建立了WorkerRunnable和FutureTask兩個實例,並把mWorker傳遞給了mFuture。我們要啓動某一任務都會執行下execute()方法,看下它的源碼,如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
調用了executeOnExecutor,繼續看下實現:public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
...
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在這裏我們看到了onPreExecute(),因此可以看出onPreExecute()是首先被執行的,然後調用了exec.execute(),從上面的代碼中我們看到exec就是sDefaultExecutor,那麼sDefaultExecutor是什麼東東呢?我們看到了如下代碼:public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
……
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
也就是我們實際調用的是SerialExecutor的execute()方法,看下實現:private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
首先把任務放到mTasks這個集合裏面;然後判斷mActivie如果爲空,就調用scheduleNext ()方法。mActivie爲null的意思是當前沒有任務在執行,如果mActivie!=null,那麼說明當前有任務正在執行,那麼只要把任務添加到mTasks裏面即可。
因爲任務執行完畢後,會再次調用scheduleNext()方法的,就是
finally {
scheduleNext();
}
這樣就形成了一種鏈狀調用結構,只要mTasks裏面還有任務,就會不斷逐一調用,如果後面有任務進來,就只要添加到mTasks裏面即可。
這裏給execute()傳遞的參數是mFuture,所以會執行到mFuture的run()方法,而run()方法最終會調用如下方法:
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
我們看到有調用callable.call(),而callable就是mWorker了,所以這裏就執行到了下面的函數:public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return postResult(doInBackground(mParams));
}
postResult(doInBackground(mParams))這一句就走到了doInBackground()方法了,並且將它的返回值傳給了postResult,那我們看下postResult的具體實現吧!private Result postResult(Result result) {
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
原來是使用了sHandler來發送消息,那我們再看下處理消息的地方: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:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
收到MESSAGE_POST_RESULT就執行finish(),收到MESSAGE_POST_PROGRESS就執行onProgressUpdate()。我們看下finish()的實現:private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
這裏做了一個判斷,根據任務是否被取消來調用不同的方法。那MESSAGE_POST_PROGRESS消息是什麼時候發送的呢?猜想一下,肯定是在publishProgress()裏面,看下實現:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
至此AsyncTask相關的流程就講的差不多了,可以看到AsyncTask的任務執行是單線程的,關於AsyncTask的任務執行是單線程實現還是多線程實現是經歷了一番變化的,較早的版本是單線程實現,從Android2.X開始,Google把它改爲多線程實現,後來發現,多線程實現的話,會有很多需要保證線程安全的額外工作留給開發者,所以從Android3.0開始,又把默認實現改爲單線程了。使用多線程實現的話,有一個很大的缺陷,那就是線程池總大小是128,也就是如果任務數量超過了128,那麼程序就會崩潰,如果有興趣可以看我下面一篇文章《Android AsyncTask兩種線程池分析和總結 》。