AsyncTask是Android中除Handler外另一種方便處理耗時任務后的執(zhí)行UI操作的工具類爪幻。其實這個說法本身有一定歧義怨咪,因為TsyncTask內(nèi)部原理還是用到了Handler,可以說TsyncTask是Handler和線程使用的封裝后的工具截碴。
AsyncTask有以下幾個注意事項
注意事項:
1.類的加載需要在主線程中
2.對象要在主線程中創(chuàng)建
3.execute在主線程中執(zhí)行
4.不直接調用onPostExecute,onProgressUpdate忿偷,onPostExecute,doInBackground等方法
5.execute只能執(zhí)行一次
6.Android3.0以后串行執(zhí)行
源碼解析
這些注意事項后續(xù)都會結合AsyncTask的源碼進行解析臊泌。首先來看構造方法:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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);
}
}
};
}
mWorker是一個WorkerRunnable鲤桥,WorkerRunnable是一個實現(xiàn)Callable的抽象類,內(nèi)部定義了Params[] mParams;變量渠概。這個mWorker其實就是執(zhí)行耗時任務的子線程茶凳。mFture就是mWorker的FutureTask。AsyncTask的執(zhí)行需要調用execute()方法播揪,然后調用了executeOnExecutor方法贮喧。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
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;
}
首先會判斷AsyncTask的mStatus,RUNNING和FINISHED都會報錯剪芍,一旦調用該方法狀態(tài)就會是RUNNING塞淹,AsyncTask執(zhí)行完后mStatus就會是FINISHED,這就解釋了注意事項5:execute只能執(zhí)行一次罪裹。onPreExecute()方法需要執(zhí)行在doInBackground之前的UI操作需要在UI線程中饱普,所以executeOnExecutor方法必須要在UI線程中。這就解釋了注意事項3:execute在主線程中執(zhí)行状共。sDefaultExecutor的execute(mFuture)繼續(xù)執(zhí)行異步類的執(zhí)行操作套耕。sDefaultExecutor是SerialExecutor對象,從命名上可以猜測sDefaultExecutor的一個單執(zhí)行的串行線程池峡继。還是從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);
}
}
}
SerialExecutor 內(nèi)部定義了名為mTasks的ArrayDeque來存儲線程冯袍,可以從execute方法中看出,每次執(zhí)行SerialExecutor 只是從mTasks按順序取出一個線程轉交給THREAD_POOL_EXECUTOR去執(zhí)行碾牌】捣撸可以看出AsyncTask有2個線程池分別是SerialExecutor和THREAD_POOL_EXECUTOR。SerialExecutor不做具體的線程執(zhí)行操作而是每次取出一個線程給THREAD_POOL_EXECUTOR只做為“排隊線程池“”使用舶吗,而THREAD_POOL_EXECUTOR才是執(zhí)行線程操作的“執(zhí)行線程池”征冷。這就解釋了注意事項:6THREAD_POOL_EXECUTOR是AsyncTask的靜態(tài)變量。
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
從上述參數(shù)可以知道THREAD_POOL_EXECUTOR的核心線程數(shù)的CPU的量誓琼,最大線程數(shù)的2倍CPU數(shù)量+1检激,非核心線程的閑置超時時長為1秒。
THREAD_POOL_EXECUTOR執(zhí)行的構造方法的mFuture腹侣。這就需要重新回看構造方法叔收,主要分析下mWorker:
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
這里可以發(fā)現(xiàn)doInBackground()方法,所以doInBackground()本質上還是運行在子線程中去執(zhí)行耗時任務傲隶。doInBackground()方法執(zhí)行完之后應該需要將UI數(shù)據(jù)傳給UI線程去更新UI饺律,postResult()方法接受了result數(shù)據(jù),所以這里應該就說執(zhí)行向UI線程傳遞的任務伦籍±渡梗看下postResult()是如何實現(xiàn)該功能的腮出。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
Handler!AsyncTask的消息傳遞也是使用的Handler芝薇。既然用的Handler胚嘲,如果要做UI操作,Handler必須要在主線程中創(chuàng)建洛二。而sHandler是靜態(tài)成員變量馋劈,在類加載的時候已經(jīng)初始化了,這就解釋了注意事項1:類的加載需要在主線程中晾嘶。
private static InternalHandler sHandler = new InternalHandler();
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_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
這里Handler做了2個處理妓雾,一是更新精度,這個msg.what的通過publishProgress()方法觸發(fā)垒迂;二是處理耗時計算完成或者結束的情況械姻。執(zhí)行結束的話就會調用result.mTask.onPostExecute()方法。result.mTask就是AsyncTask机断。onPostExecute()方法需要在主線程中調用萎战,這解釋了注意事項2:AsyncTask對象要在主線程中創(chuàng)建州藕。至于注意事項4:不直接調用onPostExecute换棚,onProgressUpdate浓领,onPostExecute,doInBackground等方法奋蔚。這個比較好理解她混,因為這些方法在AsyncTask內(nèi)部都已經(jīng)調用了,手動調用會打亂AsyncTask的工作流程泊碑。
潛在問題
AsyncTask的內(nèi)存泄漏原理和Handler類似坤按。具體處理就不詳細說明了。有些讀者可能說在Activity的onDestory()中將AsyncTask取消掉馒过,這里就講些下AsyncTask的取消AsyncTask.cancel(mayInterruptIfRunning);晋涣。調用cancel方法并不能真正立即把task取消掉,而只是把task的狀態(tài)置為Cancel而已,可以通過isCancelled()方法來判斷,然后在doingbackground或其他方法中判斷是否被取消,然后做相應的處理,具體根據(jù)開發(fā)者需要去編寫沉桌。
需要注意的是調用的cancel方法不會執(zhí)行onPostExecute(result),而是onCancelled(result)算吩。另一個問題就是在屏幕旋轉等造成Activity重新創(chuàng)建時AsyncTask數(shù)據(jù)丟失的問題留凭。當Activity銷毀并創(chuàng)新創(chuàng)建后,還在運行的AsyncTask會持有一個Activity的非法引用即之前的Activity實例偎巢。導致onPostExecute()沒有任何作用蔼夜。