工作流程簡(jiǎn)述
AsyncTask 涉及的知識(shí)點(diǎn)有 Handler撤摸,Thread框冀,Callable顶霞,F(xiàn)utureTask等蓝翰。
大體流程圖如下:
簡(jiǎn)單介紹下:
AsyncTask 的構(gòu)造器創(chuàng)建了一個(gè) mFuture(FutureTask) 和 mWorker(WorkerRunnable),mFuture 重寫了 done 方法贯钩,mWorker 實(shí)現(xiàn)了 call 方法募狂,將 mWorker 傳入 mFuture。
構(gòu)建完 AsyncTask 對(duì)象角雷,就可以執(zhí)行 execute 方法祸穷,execute 方法中調(diào)用了 executeOnExecutor,并傳入了默認(rèn)的 sDefaultExecutor 勺三,sDefaultExecutor 默認(rèn)是模擬的單線程(下面會(huì)詳細(xì)說怎么模擬的)雷滚,AsyncTask 的任務(wù)只會(huì)一個(gè)一個(gè)執(zhí)行。也可以傳入自定義的線程檩咱,并發(fā)執(zhí)行任務(wù)揭措。在 executeOnExecutor 方法中胯舷,執(zhí)行下載前的準(zhǔn)備方法 onPreExecute(); 刻蚯。然后 Executor 執(zhí)行 mFuture(FutureTask),此時(shí)會(huì)調(diào)用 mFuture 的 run();
桑嘶。run()
方法中會(huì)調(diào)用 mWorker 中的 call();
炊汹,此時(shí)在子線程中會(huì)調(diào)用 doInBackground(),我們的耗時(shí)任務(wù)就是在這里實(shí)現(xiàn)的逃顶。耗時(shí)任務(wù)結(jié)束后讨便,拿到返回值調(diào)用 postResult()
充甚,這里創(chuàng)建了一個(gè) Message (what = MESSAGE_POST_RESULT)帶上結(jié)果 result 發(fā)送給了處理消息的 InternalHandler 對(duì)象。sHandler 收到消息之后如果 what 是 MESSAGE_POST_RESULT霸褒,則判斷任務(wù)是否被取消 isCancelled()
伴找。如果取消了則調(diào)用 onCancelled(result) 沒有取消則調(diào)用 onPostExecute(result)。
在 doInBackground()
中废菱,我們還可以調(diào)用 publishProcess()
方法技矮,在主線程中刷新進(jìn)度,方法中創(chuàng)建了一個(gè) Message (what = MESSAGE_POST_PROGRESS)帶上進(jìn)度值 value殊轴,同樣發(fā)送給 sHandler 處理衰倦,sHandler 處理消息,如果 what 是 MESSAGE_POST_PROGRESS 則調(diào)用 onProgressUpdate(Progress... values) 方法旁理。
onPreExecute();
樊零,onCancelled(result)
,onPostExecute(result)
孽文,onProgressUpdate(Progress... values)
這些方法都是在主線程中執(zhí)行驻襟,只有 doInBackground()
是子線程中執(zhí)行。
AsyncTask 概念
AsyncTask 芋哭,(以下翻譯自官方文檔)能夠正確塑悼,容易的使用 UI 線程。這個(gè)類允許你執(zhí)行后臺(tái)操作并將結(jié)果呈現(xiàn)在 UI 線程上楷掉,不用你去操作 threads 和 handlers厢蒜。
AsyncTask 被設(shè)計(jì)為一個(gè) Thread 和 Handler 的輔助類,并不是構(gòu)建線程的框架烹植。理想情況下斑鸦,AsyncTask 被用于短時(shí)間操作(大部分是幾秒鐘的),如果你需要保持線程長時(shí)間運(yùn)行草雕,非常推薦你使用 java.util.concurrent 包的一些 APIs巷屿,如 Executor,ThreadPoolExecutor 和 FutureTask墩虹。
異步任務(wù)由在后臺(tái)線程運(yùn)行嘱巾,在 UI 線程呈現(xiàn)結(jié)果的計(jì)算定義。異步任務(wù)由三個(gè)普通類型诫钓,Params旬昭,Progress,Result 和四個(gè)步驟菌湃,分別是 onPreExecute, doInBackground, onProgressUpdate and onPostExecute 來定義问拘。
代碼示例:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
@Override
protected void onPreExecute() {
//準(zhǔn)備工作,在主線程。
}
protected Long doInBackground(URL... urls) {
//耗時(shí)操作骤坐,在子線程绪杏。
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));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
//顯示進(jìn)度,在主線程
setProgressPercent(progress[0]);
}
@Override
protected void onCancelled(Float result) {
//任務(wù)被取消會(huì)調(diào)用這個(gè)方法纽绍,不會(huì)調(diào)用 onPostExecute 蕾久,在主線程。
showDialog("Cancelled " + result + " bytes");
}
@Override
protected void onPostExecute(Long result) {
//任務(wù)完成拌夏,沒有被取消腔彰,調(diào)用這個(gè)方法,在主線程辖佣。
showDialog("Downloaded " + result + " bytes");
}
}
//使用
new DownloadFilesTask().execute(url1, url2, url3);
接下來結(jié)合這段代碼示例霹抛,前面的工作流程圖和源代碼,看下 AsyncTask 如何在 UI 線程和子線程之間切換卷谈,如何使用三個(gè)范型杯拐,四個(gè)步驟如何調(diào)用。
先看 AsyncTask 的構(gòu)造方法:
public AsyncTask() {
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 {
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);
}
}
};
}
構(gòu)造方法中創(chuàng)建了一個(gè) mWorker(WorkerRunnable)世蔗,它實(shí)現(xiàn)的是 Callable 接口端逼,和 Runnable 接口的區(qū)別是,實(shí)現(xiàn)方法有返回值 V call() throws Exception;
污淋,call() 方法的代碼后面會(huì)做說明顶滩。
同時(shí)還創(chuàng)建了一個(gè) mFuture(FutureTask),并將 mWorker 做為參數(shù)傳入寸爆。mFuture 重寫了 done() 方法礁鲁,這里也是后面調(diào)用到再做說明。
AsyncTask 創(chuàng)建出來后赁豆,需要調(diào)用 public final AsyncTask<Params, Progress, Result> execute(Params... params)
方法來這執(zhí)行這個(gè)任務(wù)仅醇。
這個(gè)方法里調(diào)用的是 executeOnExecutor(sDefaultExecutor, params);
,傳入的是 sDefaultExecutor 和 params 對(duì)應(yīng)的參數(shù)魔种。我們先看 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);
}
}
}
sDefaultExecutor 的值是一個(gè)內(nèi)部類 SerialExecutor 的對(duì)象 SERIAL_EXECUTOR,這個(gè)類實(shí)現(xiàn) Executor 接口节预。為什么說它是模擬的單線程呢叶摄?一開始我以為它是一個(gè)單線程的線程池,看了代碼之后發(fā)現(xiàn)并不是安拟,任務(wù)是 THREAD_POOL_EXECUTOR 這個(gè)自定義線程池執(zhí)行的蛤吓。任務(wù)維護(hù)在 ArrayDeque 這個(gè)隊(duì)列中,SERIAL_EXECUTOR 執(zhí)行 execute 方法就會(huì)創(chuàng)建一個(gè)任務(wù)放入這個(gè)隊(duì)列去扣。當(dāng) mActive == null
的時(shí)候柱衔,說明之前隊(duì)列中還沒有任務(wù),然后執(zhí)行 scheduleNext();
,從隊(duì)列中取出任務(wù)賦值給 mActive,并由 THREAD_POOL_EXECUTOR 來執(zhí)行任務(wù)闭树,這時(shí)會(huì)執(zhí)行任務(wù)的 run() 方法琐旁,而 run() 方法中又會(huì)執(zhí)行傳進(jìn)來的那個(gè)任務(wù) final Runnable r
,執(zhí)行完后窿锉,同樣調(diào)用 scheduleNext();
再去取下一個(gè)任務(wù),如此循環(huán),直到隊(duì)列中沒有任務(wù)為止王浴。
THREAD_POOL_EXECUTOR 這個(gè)自定義線程的創(chuàng)建過程看如下代碼:
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 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);
介紹完 sDefaultExecutor 后,回到主線梅猿,看方法 public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... 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;
}
方法中,先是校驗(yàn)任務(wù)狀態(tài)袱蚓,如果是 RUNNING 已運(yùn)行或是 FINISHED 已完成都會(huì)拋出異常钞啸。然后,修改任務(wù)狀態(tài)為 RUNNING喇潘。之后再執(zhí)行 onPreExecute(); 方法体斩,此時(shí)重要的四個(gè)步驟的第一步就被執(zhí)行了。之后將傳入的參數(shù) params 交給 mWorker颖低,執(zhí)行 mFuture絮吵。
此時(shí),和我們之前介紹的 sDefaultExecutor 執(zhí)行過程 和 AsyncTask 的構(gòu)造方法就要聯(lián)系起來了忱屑。調(diào)用 exec.execute(mFuture);
方法將 mFuture 包裝成任務(wù)放入隊(duì)列(前面說過的)蹬敲,隨后被執(zhí)行 mFuture.run()。因?yàn)?FutureTask 實(shí)現(xiàn)了 Runnable 接口莺戒,所以會(huì)有對(duì)應(yīng)的 run() 方法粱栖。
我們找到 FutureTask 類來看下它的 run() 方法是如何實(shí)現(xiàn)的。
public class FutureTask<V> implements RunnableFuture<V> {
...
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
protected void set(V v) {
if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
outcome = v;
U.putOrderedInt(this, STATE, NORMAL); // final state
finishCompletion();
}
}
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (U.compareAndSwapObject(this, WAITERS, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
...
}
代碼中的 callable 就是前面構(gòu)造器中傳入 mFuture 的 mWorker脏毯。Callable<V> c = callable; result = c.call();
可以看到 mWorker 被調(diào)用了闹究,隨后又調(diào)用 set(result);
,將結(jié)果賦值給
outcome,調(diào)用 finishCompletion()
食店,最后調(diào)用 done()渣淤。done 方法中主要是調(diào)用 postResultIfNotInvoked(get());
來校驗(yàn)(mTaskInvoked標(biāo)識(shí)位)如果任務(wù)沒有執(zhí)行,也保證執(zhí)行postResult(result);
方法吉嫩,把結(jié)果返回給主線程价认。
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
此時(shí) mWorker 的 call() 和 mFuture 的 done() 方法都被調(diào)用了。
我們?cè)诜厝タ?mWorker 的 call() 方法自娩。
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);
}
};
首先設(shè)置任務(wù)被調(diào)用的標(biāo)識(shí)為 true用踩,設(shè)置線程優(yōu)先級(jí)了 THREAD_PRIORITY_BACKGROUND渠退,再調(diào)用方法 doInBackground(mParams),這是四個(gè)步驟的第二步方法被調(diào)用脐彩,耗時(shí)任務(wù)開始執(zhí)行碎乃。獲取到返回的結(jié)果后,調(diào)用 postResult(result);
方法惠奸。
對(duì) Binder.flushPendingCommands();
作用并不清楚梅誓。我們先看 postResult。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
創(chuàng)建一個(gè) Message佛南,what 是 MESSAGE_POST_RESULT梗掰,用返回值創(chuàng)建一個(gè) AsyncTaskResult 對(duì)象做為message 的 obj,并發(fā)送出去嗅回,交給 sHandler 來處理及穗,我們看 Handler 處理消息的代碼。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}
}
}
handleMessage 方法中處理收到的消息绵载,如果消息是 MESSAGE_POST_RESULT拥坛,調(diào)用 finish 方法。 finish 方法中根據(jù)任務(wù)是否被取消尘分,來執(zhí)行不同的方法猜惋,如果取消則執(zhí)行 onCancelled(result);,沒有取消執(zhí)行 onPostExecute(result);培愁,此時(shí) AsyncTask 的第四步驟就走完了著摔。整個(gè)任務(wù)執(zhí)行完畢《ㄐ可以看到 onCancelled 和 onPostExecute 只會(huì)執(zhí)行一個(gè)谍咆。
如果是 MESSAGE_POST_PROGRESS 的消息,則執(zhí)行 onProgressUpdate(result.mData) 方法私股,這個(gè)是第三個(gè)步驟摹察,這個(gè)消息是怎么來的呢?
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
原來我們可以在工作線程中調(diào)用這個(gè)方法倡鲸,也就是在 doInBackground 中使用供嚎。如果任務(wù)沒有取消,就會(huì)創(chuàng)建一個(gè) what 是 MESSAGE_POST_PROGRESS峭状,將進(jìn)度值封裝成一個(gè) AsyncTaskResult 對(duì)象做為 obj 的 Message 給 Handler 處理克滴。
最后看下,任務(wù)是怎么被取消的优床,調(diào)用如下方法:
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
修改是否取消的標(biāo)識(shí)位劝赔,調(diào)用 mFuture 的 cancel 方法。mFuture 的 cancel 方法中胆敞,如果有當(dāng)前任務(wù)的線程則會(huì)調(diào)用這個(gè)線程的 interrupt 的方法并也會(huì)調(diào)用 finishCompletion();
着帽,判斷任務(wù)是否調(diào)用杂伟,發(fā)送 postResult。
總結(jié)
AsyncTask 不僅能幫我們簡(jiǎn)單的完成異步任務(wù)的操作仍翰,還就如何更好更準(zhǔn)確的使用 Handler 和 Thread 做出了示范赫粥,從其中能學(xué)到很多有用的知識(shí)。