[安卓筆記]AsyncTask源碼剖析
前言: 初學AsyncTask時,就想研究下它的實現源碼,怎奈源碼看了好幾遍都沒看懂,於是擱置了。最近心血來潮,又看了一些源碼,如HandlerThread,IntentService,AsyncQueryHandler等,收穫頗深,於是乎想回頭再研究下AsyncTask,沒想到這次居然很容易看懂了。。。 本文: 註:1.讀者閱讀本文前,必須對android的Handler機制以及j.u.c中的線程池有所瞭解;2.AsyncTask使用方式不再贅述。3.不同版本AsyncTask內容有些不同.
首先明確AsyncTask是個抽象類別,接受三個泛型參數,分表代表任務所需參數類型,任務進度類型,結果類型。
public abstract class AsyncTask
開發人員繼承AsyncTask後必須重寫doInbackground方法,其他方法如onPostExecute等按需重寫。
其內部有個靜態全域的線程池變數THREAD_POOL_EXECUTOR,AsyncTask的doInbackground中的任務就是由此線程池執行。
public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
ThreadPoolExecutor各參數含義如下,CORE_POOL_SIZE為核心線程數量,MAXIMUM_POOL_SIZE為線程池最大線程數量,KEEP_ALIVE參數表明當線程池的線程數量大於核心線程數量,那麼空閑時間超過KEEP_ALIVE時,超出部分的線程將被回收,sPoolWorkQueue是任務隊列,儲存的是一個個Runnable,sThreadFactory是線程工廠,用於建立線程池中的線程。所有這些參數在AsyncTask內部都已經定義好:
private static final int CORE_POOL_SIZE = 5; private static final int MAXIMUM_POOL_SIZE = 128; private static final int KEEP_ALIVE = 1; 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 BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(10);
AsyncTask並不是直接使用上述線程池,而是進行了一層“封裝”,這個類就是SerialExecutor,這是個串列的線程池。
/** * An {@link Executor} that executes tasks one at a time in serial * order. This serialization is global to a particular process. */ public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
看其具體實現:
private static class SerialExecutor implements Executor { final ArrayDeque mTasks = new ArrayDeque(); 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); } } }
調用execute方法時,先將Runnable封裝一下(即加上try-finally塊)然後排入佇列,接著判斷當前mActive是否為空白,第一次調用,此值為空白,所以會調用scheduleNext方法,從隊列中取出頭部的任務,交給線程池THREAD_POOL_EXECUTOR處理,而處理過程是這樣的,先執行原先的Runnable的run方法,接著再執行scheduleNext從隊列中取出Runnable,如此迴圈,直到隊列為空白,mActive重新為空白為止。我們發現,經過這樣的處理,
所有任務將串列執行。
所以我們需要注意,如果兩個AsyncTask都調用execute時,如果其中一個AsyncTask任務執行時間非常長,這將導致另一個AsyncTask的任務排隊等候,無法執行,因為它們共用同一個線程池且池是串列執行任務的。
接著看其他成員:
private static final int MESSAGE_POST_RESULT = 0x1;//當前訊息類型--->任務完成訊息 private static final int MESSAGE_POST_PROGRESS = 0x2;//當前訊息類型-->進度訊息 private static final InternalHandler sHandler = new InternalHandler(); private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//預設線程池 private final WorkerRunnable mWorker; private final FutureTask mFuture; private volatile Status mStatus = Status.PENDING;//目前狀態 private final AtomicBoolean mCancelled = new AtomicBoolean(); private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
sDefaultExecutor指明預設的線程池是串列池,mStatus指明當前任務狀態,Status是個枚舉類型:
public enum Status { /** * Indicates that the task has not been executed yet. */ PENDING,//等待執行 /** * Indicates that the task is running. */ RUNNING,//執行 /** * Indicates that {@link AsyncTask#onPostExecute} has finished. */ FINISHED,//執行結束 }
重點看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 result.mTask.finish(result.mData[0]); break; case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break; } } }
InternalHandler繼承自Handler,並複寫了handleMessage,該方法根據訊息類型做不同處理,如果任務完成,則調用finish方法,而finish方法根據任務狀態(取消or完成)調用onCancelled或者onPostExecute,這兩個是回調方法,通常我們會在onPostExecute中根據任務執行結果更新UI,這也是為什麼文檔中要求AsyncTask必須在UI線程中建立的原因,我們的Handler須與UI線程的Looper綁定才能更新UI,並且子線程預設是沒有Looper的。
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
如果是更新進度的訊息(MESSAGE_POST_PROGRESS),那麼調用onProgressUpdate方法。
接下來我們關注的問題是
doInbackground方法在何處被調用?聯想AsyncTask的使用,當我們建立好一個AsyncTask執行個體後,我們將調用其execute方法,所以,doInbackground方法應該在execute方法中執行,找到其實現:
public final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
並沒有直接調用doInbackground,而是調用了executeOnExecutor方法:
public final AsyncTask 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方法的作用是使用指定的線程池執行任務,這裡當然使用的是sDefaultExecutor,也就是SerialExecutor,但是我們注意到這個方法是公用的,也就是說
我們可以手動設定線程池,讓AsyncTask並存執行起來,最簡單的方法就是使用內部定義好的THREAD_POOL_EXECUTOR。 executeOnExecutor方法首先檢查目前狀態,如果不是Pending狀態則拋出異常,緊接著修改狀態為Running態,接著調用onPreExecute方法(預先處理,相信大家不陌生)。
關鍵代碼是exec.execute(mFuture)這一句,這行代碼的作用是執行mFuture中定義好的任務,mFuture為FutureTask類型,FutureTask是Runnable的實作類別(j.u.c中的),所以可以作為線程池execute方法的參數,我們找到其定義:
mFuture = new FutureTask(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 occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } };
我們都知道,FutureTask構造時必須傳入一個Callable的實作類別,線程最終執行的是Callable的call方法(不明白的請看java線程並發庫),所以mWorker必然是Callable的實作類別:
private static abstract class WorkerRunnable implements Callable { Params[] mParams; } mWorker = new WorkerRunnable() { public Result call() throws Exception { mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } };
最終,我們在WorkRunnable方法中找到了doInBackground方法,曆經艱辛啊!! 剩下最後一個問題:
訊息是如何發送給Handler的?
1.任務執行完畢的訊息是通過postResult方法發送的:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; }
這個AsyncTaskResult封裝了資料域和對象本身:
private static class AsyncTaskResult { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }2.任務進度更新訊息是通過publishProgress方法發送的:
protected final void publishProgress(Progress... values) { if (!isCancelled()) { sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult(this, values)).sendToTarget(); } }
至此,AsyncTask源碼分析完畢!
稍微做個
總結:
1.AsyncTask是對線程池和Handler的封裝,但是預設情況下任務是串列執行的,需注意,多個AsyncTask執行個體共用同一線程池(線程池是static的);
2.高並發任務並不推薦使用AsyncTask,而應該改用線程池;
3.務必在主線程中建立AsyncTask,因為AsyncTask內部的Handler在建立時必須要綁定主線程的Looper;
4.只能在doInBackground方法中執行耗時任務,其他方法如onPreExecute、onPostExecute等運行於主線程上.