使用 AsyncTask
能夠更加簡單地實現任務的異步執行,以及任務執行完畢以後與主線程的交互。它被設計用來執行耗時比較短的任務,一般是幾秒種的那種,若是要執行耗時比較長的任務,那麼就應該使用 JUC 包中的框架,好比 ThreadPoolExecutor
和 FutureTask
等。java
AsyncTask 用來在後臺線程中執行任務,當任務執行完畢以後將結果發送到主線程當中。它有三個重要的泛類型參數,分別是 Params
、Progress
和 Result
,分別用來指定參數、進度和結果的值的類型。 以及四個重要的方法,分別是 onPreExecute()
, doInBackground()
, onProgressUpdate()
和 onPostExecute()
。 這四個方法中,除了 doInBackground()
,其餘三個都是運行在UI線程的,分別用來處理在任務開始以前、任務進度改變的時候以及任務執行完畢以後的邏輯,而 doInBackground()
運行在後臺線程中,用來執行耗時的任務。android
一種典型的使用方法以下:git
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
if (isCancelled()) break;
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
@Override
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
複製代碼
上面說 AsyncTask
有4個重要的方法,這裏咱們覆寫了3個。doInBackground()
運行在線程當中,耗時的任務能夠放在這裏進行;onProgressUpdate()
用來處理當任務的進度發生變化時候的邏輯;onPostExecute()
用來處理當任務執行完畢以後的邏輯。另外,這裏咱們還用到了 publishProgress()
和 isCancelled()
兩個方法,分別用來發布任務進度和判斷任務是否被取消。github
而後,咱們能夠用下面的方式來使用它:框架
new DownloadFilesTask().execute(url1, url2, url3);
複製代碼
使用AsyncTask的時候要注意如下幾點內容:異步
execute()
方法必須在UI線程中被調用;onPreExecute()
, doInBackground()
, onProgressUpdate()
和 onPostExecute()
;execute()
方法只能被調用一次;Android 1.6 以前,AsyncTask 是串行執行任務的;1.6 採用線程池處理並行任務;從 3.0 開始,又採用一個線程來串行執行任務。 3.0 以後能夠用 executeOnExecutor()
來並行地執行任務,若是咱們但願在3.0以後能並行地執行上面的任務,那麼咱們應該這樣去寫:ide
new DownloadFilesTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, url1, url2, url3);
複製代碼
這裏的 AsyncTask.THREAD_POOL_EXECUTOR
是 AsyncTask 內部定義的一個線程池,咱們可使用它來將 AsyncTask 設置成並行的。oop
當初始化一個 AsyncTask 的時候,全部的重載構造方法都會調用下面的這個構造方法。這裏作了幾件事情:源碼分析
WorkerRunnable
類型的實例,而 WorkerRunnable
又繼承自 Callable
,所以它是一個能夠被執行的對象。咱們會把在該對象中回調 doInBackground()
來將咱們的業務邏輯放在線程池中執行。mWorker
而且當 mWorker
執行完畢以後會調用它的 postResultIfNotInvoked()
方法來通知主線程(不論任務已經執行完畢仍是被取消了,都會調用這個方法)。public AsyncTask(@Nullable Looper callbackLooper) {
// 1. 初始化用來發送消息的 Handler
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// 2. 封裝一個對象用來執行咱們的任務
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 回調咱們的業務邏輯
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
// 發送結果給主線程
postResult(result);
}
return result;
}
};
// 3. 初始化一個 FutureTask,而且當它執行完畢的時候,會調用 postResultIfNotInvoked 來將消息的執行結果發送到主線程中
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);
}
}
};
}
複製代碼
當這樣設置完畢以後,咱們就可使用 execute()
方法來開始執行任務了。post
咱們從 execute()
方法開始分析 AsyncTask,
@MainThread
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) { // 1.判斷線程當前的狀態
switch (mStatus) {
case RUNNING: throw new IllegalStateException(...);
case FINISHED: throw new IllegalStateException(...);
}
}
mStatus = Status.RUNNING;
onPreExecute(); // 2.回調生命週期方法
mWorker.mParams = params; // 3.賦值給可執行的對象 WorkerRunnable
exec.execute(mFuture); // 4.在線程池中執行任務
return this;
}
複製代碼
當咱們調用 AsyncTask 的 execute()
方法的時候會當即調用它的 executeOnExecutor()
方法。這裏傳入了兩個參數,分別是一個 Executor
和任務的參數 params
。從上面咱們能夠看出,當直接調用 execute() 方法的時候會使用默認的線程池 sDefaultExecutor
,而當咱們指定了線程池以後,會使用咱們指定的線程池來執行任務。
在 1 處,會對 AsyncTask 當前的狀態進行判斷,這就對應了前面說的,一個任務只能被執行一次。在 2 處會調用 onPreExecute()
方法,若是咱們覆寫了該方法,那麼它就會在這個時候被調用。在 3 處的操做是在爲 mWorker
賦值,即把調用 execute
方法時傳入的參數賦值給了 mWorker
。接下來,會將 mFuture
添加到線程池中執行。
當咱們不指定任何線程池的時候使用的 sDefaultExecutor
是一個串行的線程池,它的定義以下:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = 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 {
// 至關於對傳入的Runnable進行了一層包裝
r.run();
} finally {
// 分配下一個任務
scheduleNext();
}
}
});
// 若是當前沒有正在執行的任務,那麼就嘗試從隊列中取出並執行
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// 從隊列中取任務並使用THREAD_POOL_EXECUTOR執行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
複製代碼
從上面咱們能夠看出,咱們添加到線程池中的任務實際上並無直接交給線程池來執行,而是對其進行了處理以後才執行的,SerialExecutor 經過內部維護了雙端隊列,每當一個 AsyncTask 調用 execute()
方法的時候都會被放在該隊列當中進行排隊。若是當前沒有正在執行的任務,那麼就從隊列中取一個任務交給 THREAD_POOL_EXECUTOR
執行;當一個任務執行完畢以後又會調用 scheduleNext()
取下一個任務執行。也就是說,實際上 sDefaultExecutor
在這裏只是起了一個任務調度的做用,任務最終仍是交給 THREAD_POOL_EXECUTOR
執行的。
這裏的THREAD_POOL_EXECUTOR
也是一個線程池,它在靜態代碼塊中被初始化:
static {
// 使用指定的參數建立一個線程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
複製代碼
咱們也能夠直接將這個靜態的線程池做爲咱們任務執行的線程池而不是放在上面的隊列中被串行地執行。
上面的 WorkerRunnable
中已經用到了 postResult
方法,它用來將任務執行的結果發送給 Handler
:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = mHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
複製代碼
mHandler
會在建立 AsyncTask 的時候初始化。咱們能夠經過 AsyncTask 的構造方法傳入 Handler 和 Looper 來指定該對象所在的線程。當咱們沒有指定的時候,會使用 AsyncTask 內部的 InternalHandler
建立 Handler
:
private final Handler mHandler;
public AsyncTask(@Nullable Looper callbackLooper) {
// 根據傳入的參數建立Handler對象
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler() : new Handler(callbackLooper);
}
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
// 使用 InternalHandler 建立對象
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
// AsyncTask 內部定義 的Handler 類型
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@Override public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
// 根據傳入的消息類型進行處理
switch (msg.what) {
case MESSAGE_POST_RESULT: result.mTask.finish(result.mData[0]); break;
case MESSAGE_POST_PROGRESS: result.mTask.onProgressUpdate(result.mData); break;
}
}
}
複製代碼
上面咱們梳理了 AsyncTask 的大體過程,咱們來梳理下:
每當咱們實例化一個 AsyncTask 的時候都會在內部封裝成一個 Runnable 對象,該對象能夠直接放在線程池中執行。這裏存在兩個線程池,一個是 SerialExecutor 一個是 THREAD_POOL_EXECUTOR,前者主要用來進行任務調度,即把交給線程的任務放在隊列中進行排隊執行,而時機上全部的任務都是在後者中執行完成的。這個兩個線程池都是靜態的字段,因此它們對應於整個類的。也就是說,當使用默認的線程池的時候,實例化的 AsyncTask 會一個個地,按照加入到隊列中的順序依次執行。
當任務執行完畢以後,使用 Handler 來將消息發送到主線程便可,這部分的邏輯主要與 Handler 機制相關,能夠經過這篇文章來了解:《Android 消息機制:Handler、MessageQueue 和 Looper》。
以上就是 AsyncTask 的主要內容。
若是您喜歡個人文章,能夠在如下平臺關注我: