在Android實際開發過程設計到網路圖片下載等耗時操作,需要使用執行緒進行處理。子執行緒和UI主執行緒的互動處理主要通過Thread + Handler方式進行處理。google為了方便開發人員開發,在SDK中引入AsyncTask,使用它可以
簡單靈活地從子執行緒切換UI主執行緒。本文主要從AsyncTask原始碼角度分析該類。
AsyncTask使用
首先AsyncTask是一個抽象類,因此如果我們需要使用它,我們必須建立一個繼承它的子類.
其次我們實現該子類的時候需要指定3個泛型引數,這三個引數的用途如下:
1.Parmas:在執行AsyncTask時需要傳入的引數,可用於在後臺任務中使用。
2.Progress:後臺執行任務時,如果需要在UI執行緒中顯示進度,使用該泛型作為進度單位
3.Result:當任務執行完畢後,如果需要返回結果,使用該泛型作為返回值型別
原始碼描述如下圖:
AsyncTask簡單使用方式如下:
class SampleTask extends AsyncTask<Void, Integer, Boolean> {
.....
@Override
protected Boolean doInBackground(Void... params) {
.....
}
@Override
protected void onProgressUpdate(Integer... values) {
......
}
@Override
protected void onPostExecute(Boolean result) {
.......
}
......
}複製程式碼
如上述程式碼,定義第一個泛型為Void,表示在執行後臺任務時不需要傳入引數;定義第二個泛型是Integer,表示使用整數型別作為進度顯示型別;定義第三個
泛型為Boolean,表示使用Boolean型別作為後臺任務完成返回型別。
如上述程式碼所示,我們定義的AsyncTask重寫了3個辦法,這3個方法和之前定義的三個泛型有直接聯絡。除了這3個,還有常用的方法為onPreExecute。現在我們介紹一下這個4個方法。
1.onPreExecute:該方法會在後臺任務執行之前呼叫,其執行在UI執行緒中。主要作用是在介面上有一些初始化操作,比如顯示一個進度條。
2.doInBackground:該方法是使用AsyncTask必須重寫的方法,其執行在子執行緒中。主要作用是執行一些耗時操作。
3.onProgressUpdate:該方法是在後臺任務中呼叫punlishProgress方法之後執行,它傳入的引數泛型是Progress,上述程式碼就指代Integer。主要作用是在後臺任務執行過程中和UI執行緒實現通訊。
4.onPostExecute:該方法是後臺任務執行完畢並通過return返回時,呼叫該方法。該方法主要作用是在UI執行緒中獲知任務執行完畢或得到返回結果。
上述程式碼完整實現可以如下:
class SampleTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("當前下載進度:" + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下載成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下載失敗", Toast.LENGTH_SHORT).show();
}
}
}複製程式碼
啟動該AsyncTask如下述程式碼
new SampleTask().execute();複製程式碼
AsyncTask原始碼分析
如上述程式碼,我們發現該AsyncTask執行之前需要先new出一個物件,那我們先看AsyncTask的構建函式是怎麼樣?
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
//定義執行緒優先順序
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}複製程式碼
由AsyncTask的建構函式我們可以看到,在建構函式主要定義2個變數mWorker,mFuture。其中mWorker是Callable物件,mFuture是FutureTask物件,mWorker會作為引數傳入到mFuture中。
構建完成之後將會執行execute方法
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}複製程式碼
execute方法將呼叫executeOnExecutor方法,並傳入sDefaultExecutor以及之前定義泛型Params引數。sDefaultExecutor的定義如下
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
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);
}
}
}複製程式碼
如程式碼所示sDefaultExecutor其實就是SerialExecutor.
SerialExecutor以static修飾,那麼無論new 多少個AsyncTask都只有一個SerialExecutor物件。SerialExecutor是使用ArrayDeque這個佇列來管理Runnable。如果我們一次性啟動了很多個任務,首先在第一次執行execute方法時,會呼叫ArratDeque的offer方法將
傳入Runnable物件新增到Deque的佇列的尾部,然後判斷mActive物件是不是等於null。如果是第一次執行當然是null,那麼他們會呼叫scheduleNext方法。在這個佇列中頭部取值,並賦值給mActive,然後呼叫THREAD_POOL_EXECUTOR去執行取出來的Runnable.如果後續
還有執行緒加入在執行offer新增到佇列中。
那之後新增到ArrayDeque是怎麼執行?我們可以看到offer方法傳入的Runnable匿名類,該類中使用try finally方法。當之前的執行緒執行完成之後,在finally中呼叫了scheduleNext,執行後續的程式。該方法也就是說AsyncTask在同一個時刻只能執行一個執行緒,其他執行緒
是出於等待狀態。
也就是表示:原生的AsyncTask是序列執行執行緒的。
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case 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)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}複製程式碼
在executeOnExecutor我們看到了onPreExecute方法,它在執行後臺任務之前呼叫,證明了上文的說法。同時我們也看到一點:AsyncTask的方法在執行之後不能重複執行。
在executeOnExecutor中執行execute方法實際呼叫到SerialExecutor中execute方法,我們看到這個方法傳入的是Runnable,那這個Runnable是哪個呢?不錯是mFuture這個引數,實際上呼叫的是FutureTask中的run方法
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}複製程式碼
我們看到run方法呼叫了callable的call()方法,callable物件事實上是之前傳入的mWorker。也就是呼叫mWorker中的call中方法,該方法中會執行doInBackground這個方法,也就是我們必須重寫後臺任務方法。在call方法中最後會呼叫postResult方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}複製程式碼
postResult採用handler機制將result發到UI執行緒中,
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
}
}
}複製程式碼
之後執行finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}複製程式碼
在finish我們看到了onPostExecute方法。
那onProgressUpdate是怎麼呼叫的呢?在InternalHandler方法中我們其實看到接收到的Message也有MESSAGE_POST_PROGRESS。而這個訊息的傳送時在這個方法中實現的
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}複製程式碼
沒錯。就是publishProgress這個方法實現的。這個說明了上文所說的通過呼叫punlishProgress方法和UI執行緒中onProgressUpdate實時通訊。
擴充:如何將AsyncTask的任務執行修改為並行執行?
1.如上文所說AsyncTask預設的執行狀態是序列,那我們希望改成並行應該如何處理的呢?
我們知道AsyncTask之所以是序列執行的是因為SerialExecutor導致,那我們如果希望是並行執行,我們只需要將Executor修改成可以並行執行就可以了。
Executor exec = new ThreadPoolExecutor(15, 200, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
new SampleTask().executeOnExecutor(exec);複製程式碼
2.AsyncTask的初始化是否可以在子執行緒中執行?
可以的,但是android5.1之前版本不可以。原因是:我們知道AsyncTask和主執行緒互動式通過handler方式實現的。但是5.1之前和 5.1之後的版本的handler定義如下,我們5.1之前的版本的使用的預設執行緒的Looper,
5.1之後獲取的是UI執行緒的Looper。在5.1之前的版本如果你的AsyncTask是在子執行緒建立的,那麼很不幸,你的onPreExecute和onPostExecute並非在UI執行緒執行,而是被Handler post到建立它的那個執行緒執行;
如果你在這兩個執行緒更新了UI,那麼直接導致崩潰。
//android 5.0
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
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
//android 5.1
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
}
}
}複製程式碼