AsyncTask 類常用于處理 Android 的異步任務(wù)。
本文概括AsyncTask 的使用和簡單分析內(nèi)部實(shí)現(xiàn)原理棍郎。
本文記錄基于Android API 27顾稀。
要點(diǎn)
- AsyncTask 的創(chuàng)建可以傳入 Handler對象或 Looper對象,也可以不傳任何對象坝撑;
- AsyncTask 需要重寫其 doInBackground()方法静秆,此方法會在子線程中執(zhí)行粮揉;
- AsyncTask 的onPreExecute()會在任務(wù)正式啟動前被調(diào)用,此方法會在主線程中執(zhí)行抚笔;
- AsyncTask 的 onPostExecute(Result result) 方法用于執(zhí)行結(jié)果的發(fā)送扶认,此方法會在主線程中執(zhí)行。返回的result是doInBackground()方法的結(jié)果殊橙,如果任務(wù)被取消辐宾,此方法不會被調(diào)用;
- AsyncTask 內(nèi)部使用線程池執(zhí)行后臺任務(wù)膨蛮,使用Handler機(jī)制傳遞消息叠纹;
- AsyncTask 的實(shí)例對象只能被執(zhí)行一次;
- AsyncTask 對象可以通過調(diào)用executeOnExecutor()方法指定其執(zhí)行的Executor實(shí)現(xiàn)類敞葛,方便線程池的控制誉察;
分析
構(gòu)造方法
先看一下AsyncTask的構(gòu)造方法,構(gòu)造方法有三個惹谐,分別是:
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
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);
}
}
};
}
可以看到持偏,前面兩個構(gòu)造方法都是調(diào)用第三個構(gòu)造方法的。
構(gòu)造方法中氨肌,初始化了三個成員變量mHandler鸿秆、mWorker和mFuture。
初始化mHandler時怎囚,會判斷構(gòu)造方法是否傳入null卿叽、Handler對象或Looper對象,若有恳守,則當(dāng)前AsyncTask對象中的mHandler成員是主線程中的Handler對象(參考Handler的分析)附帽。mHandler對象只有在getHandler()方法中被調(diào)用:
private Handler getHandler() {
return mHandler;
}
而getHandler()方法只在postResult(Result result)方法中被調(diào)用:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在postResult(Result result)中,獲得了一個Message對象并用當(dāng)前的mHandler發(fā)送出去井誉。
mWorker和mFuture是線程池相關(guān)的兩個類的實(shí)例。
AsyncTask 的構(gòu)造方法中的mWorker對象的call()方法整胃,這個方法顯示設(shè)置了當(dāng)前任務(wù)棧狀態(tài)颗圣,然后設(shè)置當(dāng)前線程優(yōu)先使用后臺線程,調(diào)用doInBackground(mParams)方法屁使,這個doInBackground(mParams)方法就是需要自行實(shí)現(xiàn)的方法在岂。把doInBackground(mParams)得到的結(jié)果賦值給result,最后調(diào)用postResult(result)發(fā)送出去蛮寂。這個postResult(result)就是構(gòu)造方法中的mHandler的發(fā)送消息方法蔽午。
mFuture對象的done()方法,嘗試把FutureTask實(shí)例的執(zhí)行結(jié)果拿到酬蹋,然后調(diào)用postResult(result)方法發(fā)送出去及老。在拿result的過程會判斷當(dāng)前任務(wù)是否被中斷抽莱、拋出異常執(zhí)行異常或被任務(wù)被取消骄恶,若有食铐,則執(zhí)行記錄、拋出異成常或發(fā)送空的result虐呻。
啟動異步任務(wù)
構(gòu)造好一個AsyncTask對象后,一般會調(diào)用其execute()方法來啟動任務(wù):
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
從源碼可以看到寞秃,這個方法會執(zhí)行在主線程中斟叼。如果在子線程中調(diào)用此方法,則子線程也會變成主線程春寿。例如我這么寫:
mTvFirst.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
AsyncTask asyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
Log.d(LOG_TAG, "doInBackground當(dāng)前線程是" + Thread.currentThread().getName());
return null;
}
@Override
protected void onPreExecute() {
Log.d(LOG_TAG, "onPreExecute當(dāng)前線程是" + Thread.currentThread().getName());
}
};
asyncTask.execute();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(LOG_TAG, "run當(dāng)前線程是" + Thread.currentThread().getName());
}
}).run();
}
});
點(diǎn)擊該TextView后朗涩,打印出來的是:
04-17 04:25:32.478 5805-5805/com.erkang.gradlestudy D/EKwong: onPreExecute當(dāng)前線程是main
04-17 04:25:32.481 5805-5885/com.erkang.gradlestudy D/EKwong: doInBackground當(dāng)前線程是AsyncTask #1
04-17 04:25:37.479 5805-5805/com.erkang.gradlestudy D/EKwong: run當(dāng)前線程是main
可以看到,啟動AsyncTask的線程也是主線程堂淡,盡管是在子線程中調(diào)用的馋缅。
接下來我們看一下executeOnExecutor(sDefaultExecutor, 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;
}
這個方法用了final關(guān)鍵字,目的是不讓使用者進(jìn)行更改绢淀。
方法中萤悴,先判斷AsyncTask當(dāng)前狀態(tài)是否準(zhǔn)備狀態(tài),如果在運(yùn)行或已結(jié)束皆的,則拋出異常覆履。這說明同一個AsyncTask對象不能被啟動兩次,在運(yùn)行中狀態(tài)或已結(jié)束狀態(tài)都不可再次調(diào)用费薄。
然后把當(dāng)前狀態(tài)設(shè)置為運(yùn)行中硝全,回調(diào)onPreExecute()方法。onPreExecute()方法本身為空方法楞抡,在主線程中調(diào)用伟众,使用者可以重寫來實(shí)現(xiàn)自己想要的功能。
然后把參數(shù)賦值給mWorker召廷,調(diào)用Executor的實(shí)現(xiàn)類對象sDefaultExecutor的execute(Runnable command)方法凳厢。最后把AsyncTask本身返回。
SerialExecutor
這時候竞慢,我們看一下這個sDefaultExecutor對象是什么來的先紫,發(fā)現(xiàn)在代碼里是一個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);
}
}
}
在這個SerialExecutor類中,有一個Runnable的隊(duì)列mTasks和一個具體的Runnable對象mActive筹煮。
在它的execute(final Runnable r)方法中遮精,先把本次execute()方法傳進(jìn)來的Runnable對象和一個scheduleNext()對象組裝成一個 新的Runnable對象,放到Runnable的隊(duì)列mTasks尾部败潦。判斷當(dāng)前mActive是否為空本冲,若為空准脂,則調(diào)用scheduleNext()方法。
接下來看scheduleNext()方法眼俊,先從mTasks頭部取出一個Runnable對象賦值給mActive意狠,若取到的值不為空,則讓THREAD_POOL_EXECUTOR執(zhí)行這個mActive對象疮胖。
mActive對象的run方法分兩步环戈,第一步是調(diào)用傳進(jìn)來的Runnable實(shí)現(xiàn)類的run()方法,第二步是繼續(xù)調(diào)用scheduleNext()方法澎灸。這是一個把mTasks隊(duì)列遍歷完的意思院塞。剛才我們傳進(jìn)來的Runnable實(shí)現(xiàn)類是mFuture對象,那么就是在這里調(diào)用mFuture對象的run()方法了性昭。
FutureTask
這時候我們打開mFuture的類FutureTask看一下里面的run()方法的實(shí)現(xiàn):
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);
}
}
開頭這個是當(dāng)前狀態(tài)是否為NEW和系統(tǒng)的內(nèi)存判斷拦止,finally后面也是一些系統(tǒng)的判斷,我們著重看中間try里面的內(nèi)容好了糜颠。
這里會調(diào)起FutureTask對象內(nèi)部的callable成員的call()方法汹族,如果調(diào)用成功,會把布爾值ran設(shè)置為true其兴。從構(gòu)造方法我們可以看到顶瞒,這個callable成員就是AsyncTask中的mWorker對象。也就是線程池THREAD_POOL_EXECUTOR所激活的時候元旬,會調(diào)用mWorker的call()方法榴徐。
在FutureTask的run()方法中的這個try的步驟后面,會對callable成員是否運(yùn)行成功做判斷匀归,如果為true的話坑资,調(diào)用set(result)。繼續(xù)點(diǎn)進(jìn)去這個在FutureTask的run()方法中的這個try的步驟后面穆端,會對callable成員是否運(yùn)行成功做判斷袱贮,如果為true的話,調(diào)用set(result)方法看体啰,對內(nèi)存狀態(tài)等進(jìn)行判斷后攒巍,調(diào)用了finishCompletion()方法。我們看一下這個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
}
在這里狡赐,我們找到了這個done()方法,也就是在AsyncTask的構(gòu)造方法中初始化mFuture對象時復(fù)寫的done()方法了钦幔。
看到這里枕屉,我們就知道,在AsyncTask構(gòu)造方法中初始化的兩個成員mWorker和mFuture鲤氢,在某個任務(wù)中搀擂,是先執(zhí)行mWorker的call()方法西潘,然后才執(zhí)行mFuture的done()方法。
InternalHandler
前面將了那么哨颂,在各個步驟中調(diào)用哪些方法喷市,調(diào)用后怎么發(fā)送消息,那么威恼,消息被哪個Handler接收呢品姓?接收后怎么處理呢?我們順著AsyncTask的構(gòu)造方法箫措,可以找到這個InternalHandler類:
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;
}
}
}
可以看到腹备,這個類會根據(jù)消息的類型,通知result去更新進(jìn)度或結(jié)果斤蔓。
關(guān)于
本文為簡單的學(xué)習(xí)筆記植酥,如有錯誤,請多多指出弦牡。
我的GitHub: https://github.com/EKwongChum