這篇文章中,讓咱們從源碼的角度看一下AsyncTask
的原理,最後會根據原理總結一下使用AsyncTask
中須要注意的點。android
在AsyncTask
中,有一個線程池 THREAD_POOL_EXECUTOR
和與這個線程池相關聯的 Executor
,它負責執行咱們的任務Runnable
:bash
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
方法時,任務是如何被建立並加入到這個隊列當中的:ide
//這個靜態方法,不會回調 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
方法中作了什麼:post
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;
}
}
複製代碼
那麼結論就清晰了,整個的執行過程是下面這樣的:ui
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
方法來回調不一樣的函數:this
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製代碼
調用者經過重寫onProgressUpdate
就能夠獲得當前的最新進度,AsyncTask
最終會把結果回調到主線程當中:spa
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
,把管理的活交給系統來執行。