一、概述
這篇文章中,讓我們從原始碼的角度看一下AsyncTask
的原理,最後會根據原理總結一下使用AsyncTask
中需要注意的點。
二、原始碼解析
在AsyncTask
中,有一個執行緒池 THREAD_POOL_EXECUTOR
和與這個執行緒池相關聯的 Executor
,它負責執行我們的任務Runnable
:
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;
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<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);
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
//這是執行Runnable的地方,呼叫execute會執行它,如果當前已經有一個任務在執行,那麼就是把它放到佇列當中。
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();
}
}
});
//如果為null,那麼立刻執行它;
//如果不為null,說明當前佇列中還有任務,那麼等Runnable執行完之後,再由上面的scheduleNext()執行它。
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//第一次進來呼叫了offer方法,因此會走進去,執行上面Runnable的run()方法。
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
複製程式碼
從上面可以看出,每次呼叫sDefaultExecutor.execute
時就是執行一個任務,這個任務會被加入到ArrayDeque
中序列執行,我們看一下當我們呼叫AsyncTask
的execute
方法時,任務是如何被建立並加入到這個佇列當中的:
//這個靜態方法,不會回撥 loadInBackground 等方法。
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
//這是我們最常使用的方法,引數可以由呼叫者任意指定。
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;
}
複製程式碼
在呼叫了executeOnExecutor
之後,我們把傳入的引數賦值給了mWorker
,並把mFuture
傳入給Executor
去執行,而從下面我們可以看到mFuture
的建構函式中傳入的引數正是mWorker
,這兩個東西其實才是AsyncTask
的核心,它們是在AsyncTask
的建構函式中被初始化的:
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
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 {
//要求mWorker的call方法沒有被呼叫,否則什麼也不做。
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);
}
}
};
複製程式碼
先看一下WorkerRunnable
,實現了Callable<V>
介面,增加了一個不定引數的成員變數用來傳給 doInBackground
,這個不定引數就是我們在execute
時傳入的,呼叫call
時會執行器內部的函式,而call
時會呼叫doInBackground
方法,這個方法執行完之後,呼叫postResult
,注意到call
和done
都是在子執行緒當中執行的:
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
複製程式碼
我們主要看一下FutureTask
,還記得最開始我們的Executor
最終執行的是傳入的Runnable
的run
方法,因此我們直接看一下它的run
方法中做了什麼:
public interface Runnable {
public void run();
}
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
//我們只要關注run方法
public class FutureTask<V> implements RunnableFuture<V> {
public void run() {
Callable<V> c = callable;
if (c != null && state == NEW) {
result = c.call(); //mWorker.call()
ran = true;
}
if (ran) {
set(result);
}
}
protected void set(V v) {
finishCompletion();
}
private void finishCompletion() {
done(); //mFuture.done()
callable = null;
}
}
複製程式碼
那麼結論就清晰了,整個的執行過程是下面這樣的:
- 首先呼叫
excuter.execute(mFuture)
,把mFuture
加入到佇列當中 - 當
mFuture
得到執行時,會回撥mFuture
的run
方法 mFuture#run
是執行在子執行緒當中的,它在它所在的執行緒中執行的mWorker#call
方法mWorkder#call
呼叫了doInBackground
,使用者通過實現這個抽象方法來進行耗時操作mFuture#call
執行完後呼叫mFuture#done
在上面的過程當中,有兩個地方都呼叫了postResult
,一個是mWorkder#call
的最後,另一個是mFuture#done
,但是區別在於後者在呼叫之前會判斷mTaskInvoked
為false
時才會去執行,也就是在mWorkder#call
沒有執行的情況下,這是為了避免call
方法沒有被執行時(提前取消了任務),postResult
沒有被執行,導致使用者收不到任何回撥。
postResult
會通過InternalHandler
把當前的AsyncTask
和FutureTask
的結果回撥到主執行緒當中,之後呼叫finish
方法,它會根據呼叫者是否執行過cancel
方法來回撥不同的函式:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製程式碼
呼叫者通過重寫onProgressUpdate
就可以得到當前的最新進度,AsyncTask
最終會把結果回撥到主執行緒當中:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
private static class InternalHandler extends Handler {
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;
}
}
}
複製程式碼
三、結論
經過上面的原始碼分析,我們有這麼幾個結論:
- 靜態方法
execute(Runnable runnable)
和AsyncTask
其實沒什麼太大關係,只是用到了裡面一個靜態的執行緒池而已,AsyncTask
內部的狀態都和它無關。 - 當我們對同一個
AsyncTask
例項呼叫execute(..)
時,如果此時已經有任務正在執行,或者已經執行過任務,那麼會丟擲異常。 - 在
onPreExecute()
執行時,任務還沒有被加入到佇列當中。 doInBackground
是在子執行緒當中執行的,我們呼叫cancel
後,並不一定會立即得到onCancel
的回撥,這是因為cancel
只保證了mFuture
的done
方法被執行,有這麼幾種情況:
- 如果
mWorker
的call
函式沒有執行,那麼這時mFuture
的done
方法被呼叫時,postResultIfNotInvoked
是滿足條件的,呼叫者可以立即得到onCancel
回撥。 - 如果
mWorker
的call
呼叫了,雖然mFuture
的done
執行了,但是它不滿足條件(!mTaskInvoked.get())
,那麼會一直等到doInBackground
執行完所有的操作才通過return postResult
返回,所以我們需要在doInBackground
中通過isCancel()
來判斷是否需要提早返回,避免無用的等待。
- 在
postResult
完畢之後,onCancel
和onPostExecute
只會呼叫一個。 - 任務是和
AsyncTask
例項繫結的,而如果AsyncTask
又和Activity
繫結了,如果在執行過程中這個AsyncTask
例項被銷燬了(例如Activity
被重新建立),那麼呼叫者在新的Activity
中是無法收到任何回撥的,因為那已經是另外一個AsyncTask
了。 - 關於
AsyncTask
最大的問題其實是記憶體洩漏,因為把它作為Activity
的內部類時,會預設持有Activity
的引用,那麼這時候如果有任務正在執行,那麼 Activity 是無法被銷燬的,這其實和Handler
的洩漏類似,可以有以下這麼一些用法:
- 不讓它持有
Activity
的引用或者持有弱引用。 - 保證在
Activity
在需要銷燬時cancel
了所有的任務。 - 使
AsyncTask
的執行與Activity
的生命週期無關,可以考慮通過建立一個沒有UI
的fragment
來實現,因為在Activity
重啟時,會自動儲存有之前add
進去的Fragment
的例項,Fragment
持有的還是先前的AsyncTask
。 - 使用
LoaderManager
,把管理的活交給系統來執行。