請支持原創~~~
版本基于:Android R
前言:
將復雜作業異步化、執行緒化對于Android 的性能的提高起到了很重要的作用,Android 框架為開發提供了很多輔助程式,這一篇著重分析Android AsyncTask的原理,
原始碼分析:
1. AsyncTask 宣告
public abstract class AsyncTask<Params, Progress, Result> { ... }
AsyncTask 是一個抽象類,一個模板類,需要三個模板引數,型別分別是Params、Progress、Result;
在使用的時候需要繼承AsyncTask 并指定引數型別;
2. AsyncTask 幾個靜態變數
2.1 sThreadFactory
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());
}
};
AsyncTask 原理的關鍵因素之一,為AsyncTask 的執行緒池創建執行緒,在newThread 被呼叫到的時候就會創建Thread,詳細看ThreadPoolExecutor
2.2 THREAD_POOL_EXECUTOR
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
全域變數THREAD_POOL_EXECUTOR是AsyncTask 異步操作的執行緒池管理者和執行者,
這里先注意下ThreadPoolExecutor的構造,會將sThreadFactory 存放在成員變數threadFactory,另外,CORE_POOL_SIZE 為1,也就是行程中所有AsyncTask的物件依賴的執行緒只有一個,按照順序排隊執行task,
詳細在下面execute的時候解釋,
2.3 SERIAL_EXECUTOR
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);
}
}
}
待執行的任務在這里排隊,任務最終是會運行在執行緒池中的某個執行緒中,
- step 1:最開始mActive 為null,mTasks.offer()將任務添加后并不會執行run(),而是下面case進入scheduleNext();
- step2:當執行緒池的execute時,會觸發mActive 的run(),在執行完成會finally 會繼續下一個任務的執行,如果mTasks 內沒有任務則mActive 為null,下次execute的時候會再次回圈step 1的操作;
這里的execute 和執行緒池的execute 后面找到源頭的時候集中解釋,
3. AsyncTask 的構造
雖然有三個建構式,但是只有一個是外部可見的,其他都是hide
public AsyncTask() {
this((Looper) null);
}
/**
* @hide
*/
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
*
* @hide
*/
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
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);
}
}
};
}
3.1 AsyncTask構造必須在主執行緒中
成員變數mHandler 型別為InternalHandler,但是引數為Looper.getMainLooper(),這就要求AsyncTask 的構造必須在主執行緒中;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
3.2 mWorker 就是要在執行緒池中執行的任務
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
WorkerRunnable 其實是一個Callable,Callable 的核心函式就是call,mWorker 在實體時重寫了這個call函式,而call函式呼叫是在FutureTask 中,
3.3 mFuture 管理任務狀態和程序
mFuture 為FutureTask 類:
public class FutureTask<V> implements RunnableFuture<V> {
FutureTask 為RunnableFuture:
public interface RunnableFuture<V> extends Runnable, Future<V> {
即,FutureTask類為Runnable、Future,需要實作Runnable 的run(),也是實作Future的cancel、get V,Runnable 和Future 的原始碼,這里就不列出來了,
來看下FutureTask 的構造:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
將mWorker 存起來,以便后面通過這個callable物件呼叫mWorker的call函式,呼叫程序在下面的execute 詳細解釋,
注意:一個AsyncTask 只有一個mWorker 和mFuture,但是行程內的AsyncTask 都是要在全域的執行緒池中運行,
4. AsyncTask.execute
這里就是2.3 節說的源頭,也是到了核心部分了,execute函式在AsyncTask 中出現了多次,需要注意區別:
- AsyncTask.execute(Params)
- SerialExecutor.execute(final Runnable)
- THREAD_POOL_EXECUTOR.execute(Runnable)
來分析下AsyncTask.execute:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@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;
}
4.1 task只能執行一次
默認的AsyncTask status 為PENDING,一旦啟動本task 后,status 狀態就會發生改變,后面再次execute 的時候就會出現IllegalStateException,
4.2 在進入執行緒池之前會呼叫onPreExecute
在進入執行緒池執行task 前,會在主執行緒中執行onPreExecute(),盡可能的快點完成,不要阻塞主執行緒;
4.3 將params 帶入task
mWorks.mParams 在execute 的時候傳入,mWorks 在構造的時候并沒有引數,
4.4 呼叫SERIAL_EXECUTOR.execute
回到了2.3 節,mTasks 會新建一個Runnable,對應mFuture,
最開始mActive 為null,會執行scheduleNext,將mTasks 新建的Runnable 取出并賦值給mActive,下次的scheduleNext 會在finally() 觸發,
mActive 會被傳入THREAD_POOL_EXECUTOR.execute(),
最開始的時候肯定一堆問號的,不能著急,繼續沿著THREAD_POOL_EXECUTOR.execute 繼續分析下去:
libcore/ojluni/src/main/java/java/util/concurrent/ThreadPoolExecutor.java
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
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);
}
這里不具體分析了,代碼邏輯比較簡單,這里收SERIAL_EXECUTOR 管控,execute 的時候會呼叫addWorker(),而addWorker 中會新建一個Worker 實體,而Worker 構造:
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
有兩點:
- 將ThreadPoolExecutor.execute() 傳下來的Runnable,即SerialExecutor.mActive存到了Worker物件中;
- 回到了2.2 節,在ThreadPoolExecutor 構造的時候會將sThreadFactory 存放在成員變數threadFactory,最侄訓在這里呼叫newThread函式創建一個新的Thread,
繼續回到addWorker 中,在構造Worker 實體會會呼叫Thread.start,進而呼叫Runnable.run(),即呼叫SerialExecutor.Active.run(),而mActive 是SerialExecutor 新建的Runnable,跟mFuture 是對應關系,
OK,這樣就基本形成了一張清晰的呼叫關系:

- 黑色箭頭線是正常的呼叫流程;
- 藍色箭頭線則是簡化后的流程,最終執行緒中運行的就是WorkerRunnable 中的call();
從圖可以清晰的看到doInBackground() 是在新的執行緒中執行,只不過這個執行緒是在全域的AsyncTask 中的執行緒池中,
請注意以下性能方面的要點,
默認情況下,應用會將其創建的所有 AsyncTask 物件推送到單個執行緒中,因此,它們按順序執行,而且與主執行緒一樣,特別長的作業資料包可能會阻塞佇列,鑒于這個原因,我們建議您僅使用 AsyncTask 處理持續時間短于 5ms 的作業項,
4.5 InternalHandler 中的訊息處理
- MESSAGE_POST_PROGRESS
這個訊息是為了讓UI thread 重繪資料時使用,可以在doInBackground 中呼叫publishProgress函式實作訊息跨執行緒傳輸;
- MESSAGE_POST_RESULT
在執行緒池中doInBackground 完成后會在finally 中呼叫postResult 發送訊息告知主執行緒result;
也可以在FutureTask中干預后(cancel 呼叫)執行done 函式發送result;
5. AsyncTask.cancel(boolean)
cancel 有個引數mayInterruptIfRunning,回傳值為boolean,
- task 已經completed 或者是已經cancled 或者是因為特殊原因不能cancel 時呼叫會回傳失敗;
- 如果task 還沒有start 時,此時呼叫cancel,那task 將不再運行;
- 如果task 已經start,則根據引數mayInterruptRunning 決定是否嘗試interrunpt thread;
- 即使mayInterruptRunning 為true,也不會等待task 運行完成的,cancel 一般在主執行緒中呼叫,與mWorker.call() 是兩個執行緒,引數為true 只會呼叫thread.interrupt,而不會等待thread 完成,
interrupt是中斷執行緒,如果不了解Java的中斷機制,這樣的一種解釋極容易造成誤解,認為呼叫了執行緒的interrupt方法就一定會中斷執行緒,
其實,Java的中斷是一種協作機制,也就是說呼叫執行緒物件的interrupt方法并不一定就中斷了正在運行的執行緒,它只是要求執行緒自己在合適的時機中斷自己,每個執行緒都有一個boolean的中斷狀態(這個狀態不在Thread的屬性上),interrupt方法僅僅只是將該狀態置為true,
FutureTask 中在cancel 或者是mWorker.call() 呼叫完成后,會呼叫finishCompletion(),最侄訓回呼done,也就是AsyncTask 構造中定義的,詳見第3節,最終呼叫到postResultIfNotInvoked:
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
- 如果task 已經運行,則這個postResult 會由call() 呼叫;
- 如果task 沒有運行,說明是被cancel觸發的;
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
這個就是4.5 節中提到的MESSAGE_POST_RESULT的由來,Handler 是主執行緒中運行的,詳細看第3.1 節,
這里需要注意發送訊息時帶的引數是AsyncTaskResult<Result>(this, result),來看下處理:
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;
}
}
}
這里result 就是在postResult 傳入的,mTask 為AsyncTask,mData就是從FutureTask 傳下來的result,
最終呼叫的是AsyncTask.finish():
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
- 如果是呼叫了cancel 介面,會觸發回呼onCancelled;
- 如果是正常運行,則會觸發回呼onPostExecute;
至此,AsyncTask 剖析基本上是完成了,總結下:
- AsyncTask 是一個輕量級的訊息處理類,因為行程中都依賴執行緒池中同一個執行緒,所以task盡可能的快速處理;
- AsyncTask 中有幾個全域的變數需要關注;
- AsyncTask 通過FutureTask 管理狀態,通過SerialExecutor 管理排隊的task,通過THREAD_POOL_EXECUTOR執行task;
- AsyncTask 存在執行緒間訊息通信,例如從task thread 發送cancel 和post 訊息,而這些訊息處理,需要執行緒POOL 在AsyncTask程序中沒有創建出來,那么這個天生存在的POOL 只會是主執行緒,所以,要求AsyncTask 的構造必須是在主執行緒中;
- AsyncTask 的cancel 介面帶的引數,只是用來確認是否進行thread.interrupt(),而并不是阻塞執行緒或等待執行緒處理結束;
- AsyncTask 是一個短期的單任務,task 運行結束后如果想要再次運行,需要重新建一個AsyncTask實體,而不是單純的用原來的實體呼叫execute即可的;
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/292217.html
標籤:其他
上一篇:Android中JNI的使用方法
下一篇:Kotlin - 協程基礎及原理
