AsyncTask 的源碼分析
基礎知識學習
ThreadPoolExecutor分析
關鍵參數分析
- corePoolSize:最大的核心線程數,默認情況下,核心線程會一直存活。
- maximumPoolSize:線程池中最大線程數量(核心和非核心線程總數),如果活動線程數等於最大值是,後續的新任務還會被阻塞。
- keepAliveTime:非核心線程的保留時間,超時會被回收。
- unit:keepAliveTime參數的時間單位.
- workQueue:任務隊列,用來存儲已經提交的任務(還未執行的),通過ThreadPoolExecutor的excute方法來提交Runnable對象,將其存儲在該參數中。
- threadFactory:創建新線程。具體通過方法:Thread newThread(Runnable r)。
執行流程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-l3sAkhcZ-1587131867238)(C:\Users\80261556.ADC\Desktop\線程池.jpg)]
關鍵源碼解讀
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//獲取當前線程池的狀態以及擁有的線程池數目,高位爲狀態,低位爲數目
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
//如果當前線程數目小於核心線程數,則創建核心線程,後面的bool類型參數爲true,代表創建核心線程
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果當前線程處於running狀態,之後往任務隊列中插入新的任務
if (isRunning(c) && workQueue.offer(command)) {
//之後重新檢查,如果線程池不是running狀態,則從隊列中刪除,並且reject
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
AsyncTask 源碼解析
從execute方法調用開始,代碼如下
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
//一個task只能execute一次
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 = sDefaultExecutor = SerialExecutor
exec.execute(mFuture);
return this;
}
這裏值得注意的是sDefaultExecutor這個成員變量的默認值是SerialExecutor,這是負責調度用的線程池。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
調度線程池
關於調度的線程池代碼比較好理解,主要是將Runnable插入隊列,然後通過scheduleNext調用線程池執行隊列中的任務,每一次插入對應一次執行。
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);
}
}
}
關於mFuture,我們只需要知道在裏面會輪流調用以下幾個方法:
doInBackground(mParams)
postResult(result)
onPostExecute(Result result)
這樣,一個task就基本走完了流程,但是很多時候我們需要去在UI線程裏面更新進度,這樣我們需要在doInBackground() 中調用 publishProgress () 。那麼這是怎麼實現的呢?
Handler
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@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的使用,通過消息傳遞機制傳遞MESSAGE_POST_PROGRESS消息,可以在主線程中更新UI控件,同時我們也可以注意到改handler還有一種消息:MESSAGE_POST_RESULT,同時結合上面的代碼,我們可以發現在整個task中有三個方法是運行在主線程中的:
//所以我們也可以在這三個方法中更新UI
onCancelled(result);
onPostExecute(result);
onProgressUpdate(result.mData)
總結
綜合上述,我們可以發現AsynTask的所有對象是通過共享兩個線程池和一個handler來工作的,其職責如下表
具體 | 職責 |
---|---|
SerialExecutor | 任務隊列線程池,負責任務調度 |
THREAD_POOL_EXECUTOR | 真正執行任務的線程池 |
InternalHandler | 異步通信,實現工作線程和主線程的通信,並且實現UI更新 |
同時各個方法的調用順序如下: