前言
AsyncTask在Android編程中是一種常用的異步編程方式,那麼AsyncTask到底是什麼呢?下面我們從
基本使用到源碼分析對AsyncTask作一個全面的瞭解。
一般使用
通常我們對AsyncTask的使用是
//1.實現抽象類AsyncTask
Class MyAsyncTask extends AsyncTask{
@Override
protected Object doInBackground(Object[] params) {
return null;
}
}
//2.創建實例對象
MyAsyncTask myAsyncTask = New MyAsyncTask();
//3.調用execute方法執行任務
myAsyncTask.execute();
一般使用比較簡單,通過實現AsyncTask抽象類,然後創建一個這樣的子類實例,然後調用execute方法就可以在doInBackground進行異步處理了。
源碼分析
我們看看execute方法的具體實現(這裏針對Android-23源碼分析的,不同版本的源碼實現的方式可能不同,這裏不一一分類了)
@MainThread
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) {
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;
}
首先檢測mState的狀態,如果同一個AsyncTask實例在沒有pending的情況下,再進行execute,那麼會報出相應的異常;然後去設置mState的狀態,調用onPreExecute,去執行exec.execute(mFuture),最終的執行在這裏。那麼我們看看剛纔傳進來的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);
}
}
}
實質上是調用SerialExecutor 類的execute方法,首先向mTasks 提交offer隊列的末尾,然後從mTasks 的頭部poll出要執行的runnable.
從名字就可以看出,默認情況下,多個task是加到mTask後執行的。也就是串行執行的。如果要並行執行的畫,通過executeOnExecutor(Async.THREAD_POOL_EXECUTOR,params);
關於doInBackground、onPreExecute、onPostExecute、onProgressUpdate等幾個抽象方法的調線程和時期關係,不想做太多分析,都是模版設計模式思想,在不同地方調用罷了。比較簡單。這裏主要將線程池,這裏纔是AsyncTask的精華所在,以及以後在Android系統使用單獨的線程池時候有個很好的參照。
線程池參數解釋
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;
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
corePoolSize: 核心線程數目,即使線程池沒有任務,核心線程也不會終止(除非設置了allowCoreThreadTimeOut參數)可以理解爲“常駐線程”
maximumPoolSize: 線程池中允許的最大線程數目;一般來說,線程越多,線程調度開銷越大;因此一般都有這個限制。
keepAliveTime: 當線程池中的線程數目比核心線程多的時候,如果超過這個keepAliveTime的時間,多餘的線程會被回收;這些與核心線程相對的線程通常被稱爲緩存線程
unit: keepAliveTime的時間單位
workQueue: 任務執行前保存任務的隊列;這個隊列僅保存由execute提交的Runnable任務
threadFactory: 用來構造線程池的工廠;一般都是使用默認的;
handler: 當線程池由於線程數目和隊列限制而導致後續任務阻塞的時候,線程池的處理方式。
線程池核心調度思想
那麼,當一個新的任務到達的時候,線程池中的線程是如何調度的呢?
- 如果線程池中線程的數目少於corePoolSize,就算線程池中有其他的沒事做的核心線程,線程池還是會重新創建一個核心線程;直到核心線程數目到達corePoolSize(常駐線程就位)
- 如果線程池中線程的數目大於或者等於corePoolSize,但是工作隊列workQueue沒有滿,那麼新的任務會放在隊列workQueue中,按照FIFO的原則依次等待執行;(當有核心線程處理完任務空閒出來後,會檢查這個工作隊列然後取出任務默默執行去)
- 如果線程池中線程數目大於等於corePoolSize,並且工作隊列workQueue滿了,但是總線程數目小於maximumPoolSize,那麼直接創建一個線程處理被添加的任務。
- 如果工作隊列滿了,並且線程池中線程的數目到達了最大數目maximumPoolSize,那麼就會用最後一個構造參數handler處理;默認的處理方式是直接丟掉任務,然後拋出一個異常。
具體的實現場景描述:當有新的任務要處理時,先看線程池中的線程數量是否大於 corePoolSize,再看緩衝隊列 workQueue 是否滿,最後看線程池中的線程數量是否大於 maximumPoolSize。另外,當線程池中的線程數量大於 corePoolSize 時,如果裏面有線程的空閒時間超過了 keepAliveTime,就將其移除線程池,這樣,可以動態地調整線程池中線程的數量。