前言
AsyncTask
是一個常用的已經封裝好的異步任務類,可以更加方便地執行後臺任務以及切換主線程去更新UI。從實現上來說,它封裝了Thread
(線程池)和Handler
。
定義
一個抽象的泛型類,提供了Params, Progress, Result三個泛型參數。
- a. Params:開始異步任務執行時傳入的參數類型,對應excute()中傳遞的參數
- b. Progress:異步任務執行過程中執行進度值的類型
- c. Result:異步任務執行完成後,返回的結果類型,與doInBackground()的返回值類型保持一致
public abstract class AsyncTask<Params, Progress, Result> {
}
核心方法
結合源碼的方式的方式看一下AsyncTask
的一些核心方法
1.execute
手動調用開始執行異步任務,必須在UI線程中調用
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;
}
首先調用了onPreExecute()
方法,然後調用了exec.execute(mFuture)
方法開始執行,這裏的exec是個串行的線程池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);
}
}
}
AsyncTask
中有兩個線程池SerialExecutor(用於任務的排隊)和THREAD_POOL_EXECUTOR(用於任務的執行)。
2.onPreExecute
回到onPreExecute
方法,在主線程運行,異步任務運行之前執行,用於一些準備工作。可以選擇性重寫。
@MainThread
protected void onPreExecute() {
}
下面看看AsyncTask
的構造方法
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);
}
}
};
}
構造方法中對前面executeOnExecutor
方法中提到的mWorker
和mFuture
進行了初始化操作,然後執行了doInBackground()
方法,而後又執行了postResult(result)
方法。接下來分別講述這兩個方法。
3.doInBackground
@WorkerThread
protected abstract Result doInBackground(Params... params);
抽象方法,必須重寫自定義線程任務,接受Params
參數,返回Result結果,可以在裏面調用publishProgress()
用於更新進度信息。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
會利用Handler發個消息,MESSAGE_POST_PROGRESS標誌更新UI,然後收到消息後會在主線程調用onProgressUpdate ()
方法,完美實現了線程切換操作。
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;
}
}
}
4.onProgressUpdate
在主線程中執行,後臺進度發生改變時調用,可以選擇重寫。
@MainThread
protected void onProgressUpdate(Progress... values) {
}
上述講述了執行完doInBackground()方法,而後又執行了
postResult(result)方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
同樣也是發送了消息,前面已經貼了Handler代碼,這次會調用result.mTask.finish(result.mData[0])
方法。繼續跟
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
邏輯很簡單,如果AsyncTask
被取消執行了,就調用onCancelled()
,否則調用onPostExecute()
,將doInBackground()
的返回結果傳遞給了onPostExecute()
方法
5.onPostExecute
在主線程執行,返回doInBackground()
的執行結果,將結果顯示到UI。
@MainThread
protected void onPostExecute(Result result) {
}
6.onCancelled()
在主線程執行,當異步任務被取消時,onCancelled()會被調用,這個時候onPostExecute不會調用。
@MainThread
protected void onCancelled() {
}
重要類
前面多次提到了這兩個類WorkerRunnable
和FutureTask
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
1.WorkerRunnable
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
這裏的Callable也是任務, 與Runnable的區別是Callable<T>
存在返回值 。
2.FutureTask
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
其實就是一個任務包裝類,裏面包含Callable、增加了一些狀態標識 ,操作Callable的接口。
3.THREAD_POOL_EXECUTOR
在前面提到SerialExecutor
的scheduleNext()
方法中調用了THREAD_POOL_EXECUTOR.execute(mActive)
/**
* 源碼分析:THREAD_POOL_EXECUTOR.execute()
* 說明:
* a. THREAD_POOL_EXECUTOR實際上是1個已配置好的可執行並行任務的線程池
* b. 調用THREAD_POOL_EXECUTOR.execute()實際上是調用線程池的execute()去執行具體耗時任務
* c. 而該耗時任務則是步驟2中初始化WorkerRunnable實例對象時複寫的call()
* 注:下面先看任務執行線程池的線程配置過程,看完後請回到步驟2中的源碼分析call()
*/
// 步驟1:參數設置
//獲得當前CPU的核心數
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//設置線程池的核心線程數2-4之間,但是取決於CPU核數
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
//設置線程池的最大線程數爲 CPU核數*2+1
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
//設置線程池空閒線程存活時間30s
private static final int KEEP_ALIVE_SECONDS = 30;
//初始化線程工廠
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());
}
};
//初始化存儲任務的隊列爲LinkedBlockingQueue 最大容量爲128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
// 步驟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,
sPoolWorkQueue, sThreadFactory);
// 設置核心線程池的 超時時間也爲30s
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
串行or並行
Android 1.6之前,AsyncTask
是串行執行的,Android1.6之後採用線程池任務並行執行,從Android 3.0開始,AsyncTask又
開始串行執行,就是一個接一個執行。當然也可以通過設置executeOnExecutor(Executor)
來實現多個AsyncTask
並行。
注意點
在使用AsyncTask
時有一些問題需要注意的:
1 關於 生命週期
- 結論
AsyncTask
不與任何組件綁定生命週期 - 使用建議
在Activity
或Fragment
中使用AsyncTask
時,最好在Activity
或Fragment
的onDestory()
調用cancel(boolean)
;
2 關於 內存泄漏
- 結論
若AsyncTask
被聲明爲Activity
的非靜態內部類,當Activity
需銷燬時,會因AsyncTask
保留對Activity
的引用 而導致Activity
無法被回收,最終引起內存泄露 - 使用建議
AsyncTask
應被聲明爲Activity
的靜態內部類
3 線程任務執行結果 丟失
- 結論
當Activity
重新創建時(屏幕旋轉 /Activity
被意外銷燬時後恢復),之前運行的AsyncTask
(非靜態的內部類)持有的之前Activity
引用已無效,故複寫的onPostExecute()
將不生效,即無法更新UI操作 - 使用建議
在Activity
恢復時的對應方法 重啓 任務線程