[Toc]
基礎(chǔ)認識
AsyncTask 是基于 Handler 進行封裝的輕量級異步類,它是一個抽象類,我們要使用的時候需要實現(xiàn)其子類的以下 4 個方法
| 方法 | 描述 |
|---|---|
| onPreExecute() | 任務(wù)執(zhí)行前被調(diào)用,執(zhí)行在 UI 線程中,在這里我們做一些任務(wù)啟動前的準備 |
| doInBackground() | 執(zhí)行在新的子線程中的,做異步任務(wù)的處理 |
| onProgressUpdate() | 這個方法是在調(diào)用 publishProgress 的時候被調(diào)用的,是運行在 UI 線程的 |
| onPostExecute() | 這個方法是任務(wù)執(zhí)行完畢之后被調(diào)用的,是運行在 UI 線程中的 |
作用
- 實現(xiàn)多線程: 在工作線程中執(zhí)行任務(wù),如 耗時任務(wù)
- 異步通信、消息傳遞: 實現(xiàn)工作線程 & 主線程(UI線程)之間的通信,即:將工作線程的執(zhí)行結(jié)果傳遞給主線程,從而在主線程中執(zhí)行相關(guān)的UI操作
AsyncTask 的三種狀態(tài)
每個狀態(tài)在一個任務(wù)的生命周期中只會被執(zhí)行一次。
| 狀態(tài) | 描述 |
|---|---|
| PENDING | 等待(還沒有開始執(zhí)行任務(wù)) |
| RUNNING | 執(zhí)行中 |
| FINSHED | 完成 |
AsyncTask 的內(nèi)部執(zhí)行過程
AsyncTask 的對象調(diào)用 execute 方法,execute 內(nèi)部又調(diào)用了 executeOnExecutor ,onPreExecute 方法就是在這里被回調(diào),之后將 AsyncTask 的參數(shù)封裝成一個并發(fā)類,然后將其添加到排隊線程池(SerialExecutor)中進行排隊,如果當前有任務(wù)正在執(zhí)行,則等待,否則 THREAD_POOL_EXECUTOR 執(zhí)行該任務(wù)。在任務(wù)的執(zhí)行過程中,通過 InternalHandler 將進度 pos(MESSAGE_POST_GROGRESS)發(fā)送到主線程中,此時會調(diào)用 onProgressUpdate 方法,任務(wù)執(zhí)行完畢之后,InternalHandler 將結(jié)果 post(MESSAGE_POST_RESULT) 發(fā)送到主線程中,此時 onPostExecute 或者 onCancle 會被調(diào)用,任務(wù)執(zhí)行到這里就結(jié)束了。
基本使用
- 舉個栗子:在異步任務(wù)中每隔 1s 打印 1 ~ 10 的數(shù)值
- 我們不干預(yù)任務(wù)的執(zhí)行過程,由任務(wù)執(zhí)行完成,查看任務(wù)執(zhí)行情況;
- 任務(wù)執(zhí)行完成后,我們再點擊開始,查看任務(wù)執(zhí)行情況;
- 干預(yù)任務(wù)的執(zhí)行過程,在任務(wù)執(zhí)行期間點擊取消,查看任務(wù)執(zhí)行情況;
public class MainActivity extends AppCompatActivity {
private TextView mText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = findViewById(R.id.text);
progressAsycn1 = new ProgressAsycn();
findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressAsycn1.execute(1);
}
});
findViewById(R.id.btn_cancel).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
progressAsycn1.cancel(true);
mText.append(String.format("取消任務(wù)%s\n",new Date().toString()));
}
});
}
private ProgressAsycn progressAsycn1;
private class ProgressAsycn extends AsyncTask<Integer,Integer,String> {
// 這個方法是在啟動之前被調(diào)用的,執(zhí)行在 UI 線程中,在這里我們做一些任務(wù)啟動前的準備
@Override
protected void onPreExecute() {
super.onPreExecute();
mText.append(String.format("準備執(zhí)行%s\n",new Date().toString()));
}
// 這個方法是執(zhí)行在新的線程的中的
@Override
protected String doInBackground(Integer... params) {
for (int i = params[0]; i <= 10; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
publishProgress(i);
}
return "任務(wù)已經(jīng)執(zhí)行完畢";
}
// 這個方法是在調(diào)用 publishProgress 的時候被調(diào)用的,是運行在 UI 線程的
@Override
protected void onProgressUpdate(Integer... values) {
mText.append(String.format("工作進度:%d\n",values[0]));
}
// 這個方法是任務(wù)執(zhí)行完畢之后被調(diào)用的
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
mText.append(String.format("任務(wù)執(zhí)行完畢%s\n",new Date().toString()));
}
/**
* 異步任務(wù)被取消時回調(diào),即 AsyncTask 的對象調(diào)用了 cancel 方法
* 這個方法和 onPostExecute 互斥
* doInBackground 方法中的任務(wù)執(zhí)行完畢,才會被回調(diào)
*/
@Override
protected void onCancelled() {
mText.append(String.format("異步任務(wù)已取消%s\n",new Date().toString()));
}
}
}
-
執(zhí)行結(jié)果
- 不干預(yù)任務(wù)的執(zhí)行過程,最后的執(zhí)行結(jié)果如下
asyncTask1.png- 在 1 執(zhí)行完之后,再點擊開始,結(jié)果程序報如下錯誤
java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)- 在任務(wù)的執(zhí)行過程中,點擊取消任務(wù),調(diào)用 cancel() 方法,之后我們可以看到 onProgressUpdate() 和 onPostExecute() 方法不再被調(diào)用,但是我們?nèi)∠蝿?wù)的時候,任務(wù)還是沒有停止的,等到任務(wù)真正停止的時候,onCancelled() 方法被調(diào)用,執(zhí)行效果圖如下:
asyncTask2.png -
問題:從上面的執(zhí)行結(jié)果中,我們可以看出兩個問題
- AsyncTask 的對象只能被調(diào)用一次,再次調(diào)用的時候,會出錯
- Asynctask 調(diào)用了 cancel() 方法取消任務(wù),但是任務(wù)并沒有真正的停止
提出問題
我們在閱讀源碼之前,先給自己提一些問題,然后我們在閱讀源碼的時候,帶著問題來去找答案,這樣我們的目標才會更加明確。
- 為什么AsyncTask 的對象只能被調(diào)用一次,否則會出錯?(每個狀態(tài)只能執(zhí)行一次)
- AsyncTask 的類為什么必須在主線程加載
- AsyncTask 的對象為什么必須在主線程中創(chuàng)建
- AsyncTask 是串行執(zhí)行任務(wù)還是并行執(zhí)行任務(wù)?
- AsyncTask 調(diào)用 cancel() 任務(wù)是否立即停止執(zhí)行?onPostExecute() 還會被調(diào)用嗎?onCancelled() 什么時候被調(diào)用?
內(nèi)部源碼分析
構(gòu)造函數(shù)中做了什么
首先,我們查看在 AsyncTask 的構(gòu)造函數(shù)里面到底做了些什么
/**
* 該構(gòu)造方法必須在 UI 線程中被調(diào)用
*/
public AsyncTask() {
this((Looper) null);
}
/**
* 該構(gòu)造方法必須在 UI 線程中被調(diào)用
*
* @hide
*/
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);
// doInBackground 的調(diào)用時機
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);
}
}
};
}
從以上的代碼中,我們可以看出 AsyncTask 做了以下幾個工作:
-
初始化 handler
- 我們可以從默認調(diào)用的構(gòu)造函數(shù)中傳入的 callbackLooper 是為 null,那么 handler 初始化的時候是調(diào)用 getMainHandler() 方法的,getMainHandler() 方法的的具體代碼如下:
private static InternalHandler sHandler; private static Handler getMainHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(Looper.getMainLooper()); } return sHandler; } }- 我們從上面的代碼中可以知道,getMainHandler() 最后返回的值是 sHandler,而 sHandler 是個靜態(tài) InternalHandler 實例,而關(guān)于 InternalHandler 的實現(xiàn),我們在下面再做分析。 我們在這里需要注意的點有兩個
- 為了能夠?qū)?zhí)行環(huán)境切換到主線程中,我們要求 sHandler 必須在主線程中創(chuàng)建,所以 AsyncTask 的構(gòu)造函數(shù)必須在 UI 線程中調(diào)用
- 我們知道靜態(tài)成員會在加載類的時候進行初始化,由于 sHandler 是個靜態(tài)變量,那么我們要求 AsyncTask 類必須在 UI 線程中進行加載,否則 AsyncTask 無法正常工作
-
初始化 mWorker
- mWorker 是一個 Callable 對象,并在之后初始化 mFutrue 的時候作為參數(shù)傳入,我們在 mWorker 的代碼實現(xiàn)這里可以看到 doInBackground() 在這里被調(diào)用了(但是真正的調(diào)用時機不不是在這里進行的,而是在 SerialExecutor 的 execute 的方法中),并將結(jié)果返回給 result,并在方法結(jié)束之前調(diào)用 postResult(result)。 方法
-
初始化 mFuture
- MFuture 是一個 FutureTask 對象
execute() 中做了什么
接著如果想要啟動某一個任務(wù),就需要調(diào)用該任務(wù)的 execute() 方法,因此現(xiàn)在我們來看一看 execute() 方法的源碼
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute() 方法的比較簡單,只是調(diào)用了 executeOnExecutor 方法,那么具體的邏輯是在 executeOnExecutor 里面,我們再接著看下去:
@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;
}
- 首先,executeOnExecutor 先對當前的狀態(tài)進行判斷,由于 AsyncTask 中有三個狀態(tài)(PENDING、RUNNING、FINISHED),并且每個狀態(tài)在 AsyncTask 的生命周期中有且只能被執(zhí)行一次,如果當前的狀態(tài)不是 Status.PENDING(未執(zhí)行),那么就拋出異常。在這里也解釋了,我們 execute() 方法只能被執(zhí)行一次,再次調(diào)用會報錯的原因。
- 接著,我們可以看到 onPreExecute() 方法被調(diào)用了
- 緊接著,我們可以看到 exec.execute(mFuture) 這行代碼,而 exec 又是什么,通過查找上面的 execute 方法,我們可以看到這個 exec 是一個 sDefaultExecutor 變量,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 {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
從以上代碼可以看到,在 executeOnExecutor 中的 exce 最終調(diào)用的是 SerialExecutor 中 execute() 方法。而 execute() 這個方法的邏輯是在子線程中執(zhí)行的,而 execute 這個方法傳入的 Runnable 正是 mFuture,在 run 方法中調(diào)用了, mFuture 對象的 run。我們再找到 FutureTask 中實現(xiàn)的 run 方法的代碼,代碼如下(有省略):
public void run() {
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
try {
result = c.call();
} catch (Throwable ex) {
}
}
} finally {
// .....
}
}
從上面的代碼中我們可以看出,最終調(diào)用了 Callable 中的 call (),而這個 callball 就是我們在 executeOnExecutor 中傳入 mFuture 的 mWorker 對象。現(xiàn)在我們又重新拿出 MWorker 的代碼來看一下:
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;
}
};
我們從上面的代碼可以看到兩個主要的點,首先是 doInBackground() 方法的調(diào)用,并將結(jié)果給到 result,最后在方法結(jié)束之前調(diào)用了 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;
}
而在 postResult 中使用了 sHandler 對象發(fā)送了一條消息,消息中攜帶了 MESSAGE_POST_RESULT 常量和一個表示任務(wù)執(zhí)行結(jié)果的 AsyncTaskResult 對象。這個 sHandler 對象是 InternalHandler 類的一個實例,那么稍后這條消息肯定會在 InternalHandler 的 handleMessage() 方法中被處理。InternalHandler 的源碼如下所示:
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;
}
}
}
在 handleMessage 這里對消息的類型進行了判斷,如果這是一條 MESSAGE_POST_RESULT 消息,就會去執(zhí)行 finish() 方法,如果這是一條 MESSAGE_POST_PROGRESS 消息,就會去執(zhí)行 onProgressUpdate() 方法。那么 finish() 方法的源碼如下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果當前任務(wù)被取消掉了,就會調(diào)用 onCancelled() 方法,如果沒有被取消,則調(diào)用 onPostExecute() 方法,這樣當前任務(wù)的執(zhí)行就全部結(jié)束了。
我們注意到,在剛才 InternalHandler 的handleMessage() 方法里,還有一種 MESSAGE_POST_PROGRESS 的消息類型,這種消息是用于當前進度的,調(diào)用的正是 onProgressUpdate() 方法,那么什么時候才會發(fā)出這樣一條消息呢?相信你已經(jīng)猜到了,查看publishProgress()方法的源碼,如下所示:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
最后我們來理一下 executeOnExecutor 中的任務(wù)啟動到結(jié)束,關(guān)鍵方法的調(diào)用順序:
executeOnExecutor() -> sDefaultExecutor.execute() -> mFuture.run() -> mWorker.call() -> doInBackground() -> postResult() -> sHandler.sendMessage() -> sHandler.handleMessage() -> onPostExecute()
問題解決
-
為什么 AsyncTask 的對象只能被調(diào)用一次,否則會出錯?(每個狀態(tài)只能執(zhí)行一次)
從上面我們知道,AsyncTask 有 3 個狀態(tài),分別為 PENDING、RUNNING、FINSHED,而且每個狀態(tài)在 AsyncTask 的生命周期中有且只執(zhí)行一次。由于在執(zhí)行完 execute 方法的時候會先對 AsyncTask 的狀態(tài)進行判斷,如果是 PENDING(等待中)的狀態(tài),就會往下執(zhí)行并將 AsyncTask 的狀態(tài)設(shè)置為 RUNNING(運行中)的狀態(tài);否則會拋出錯誤。AsyncTask finish 的時候,AsyncTask 的狀態(tài)會被設(shè)置為 FINSHED 狀態(tài)。
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)");
}
}
AsyncTask 的類為什么必須在主線程加載
由于 sHandler 是一個靜態(tài)的 Handler 對象,為了能夠?qū)?zhí)行環(huán)境切換到主線程中,這就要求 sHandler 必須在主線程中創(chuàng)建,也即是 AsyncTask 必須在主線程中創(chuàng)建。由于靜態(tài)成員會在加載類的時候進行初始化,因此也要求 AsyncTask 的類必須在主線程中加載,否則 AsyncTask 無法正常工作。AsyncTask 的對象為什么必須在主線程中創(chuàng)建
因為在 AsyncTask 的構(gòu)造函數(shù)中對 handler 進行了初始化的操作,所以 AsyncTask 必須在主線程中進行創(chuàng)建,否則 AsyncTask 無法進行線程切換的工作AsyncTask 是串行執(zhí)行任務(wù)還是并行執(zhí)行任務(wù)?
在 Android 1.6 之前,AsyncTask 是串行執(zhí)行任務(wù)的,Android 1.6的時候 AsyncTask 是并行執(zhí)行任務(wù)的,Android 3.0 之后,為了避免并行錯誤,AsyncTask 又采用一個線程來串行執(zhí)行任務(wù)。AsyncTask 調(diào)用 cancel() 任務(wù)是否立即停止執(zhí)行?onPostExecute() 還會被調(diào)用嗎?onCancelled() 什么時候被調(diào)用?
任務(wù)不會立即停止的,我們調(diào)用 cancel 的時候,只是將 AsyncTask 設(shè)置為 canceled(可取消)狀態(tài),我們從以下代碼可以看出,AsyncTask 設(shè)置為已取消的狀態(tài),那么之后 onProgressUpdate 和 onPostExecute 都不會被調(diào)用,而是調(diào)用了 onCancelled() 方法。onCancelled() 方法是在異步任務(wù)結(jié)束的時候才調(diào)用的。時機是和 onPostExecute 方法一樣的,只是這兩個方法是互斥的,不能同時出現(xiàn)。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
// 線程執(zhí)行完之后才會被調(diào)用
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
AsyncTask 在使用的過程中的一些限制總結(jié)
- (1)AsyncTask 的類必須在主線程加載
- (2)AsyncTask 的對象必須在主線程中創(chuàng)建
- (3)execute 方法必須在 UI 線程中調(diào)用
- (4)不要在主線程中調(diào)用 onPreExecute 、doInbackground、onPressUpdate、onPostExecute
- (5)AsyncTask 的對象只能被調(diào)用一次,否則會出錯
- (6)AsyncTask 不太適合做太耗時的操作
- (7)在 Android 1.6 之前,AsyncTask 是串行執(zhí)行任務(wù)的,Android 1.6的時候 AsyncTask 是并行執(zhí)行任務(wù)的,Android 3.0 之后,為了避免并行錯誤,AsyncTask 又采用一個線程來串行執(zhí)行任務(wù)。
- (8)如果在 AsyncTask 中的 doInBackGround 中開啟了新的線程,我們執(zhí)行了 cancle() 方法來停止異步任務(wù),線程是不會被停止的,直到任務(wù)執(zhí)行完成為止,這個過程中,onProgressUpdate 和 onPostExecute 是不會被調(diào)用的

