[Toc]java
AsyncTask 是基於 Handler 進行封裝的輕量級異步類,它是一個抽象類,咱們要使用的時候須要實現其子類的如下 4 個方法android
方法 | 描述 |
---|---|
onPreExecute() | 任務執行前被調用,執行在 UI 線程中,在這裏咱們作一些任務啓動前的準備 |
doInBackground() | 執行在新的子線程中的,作異步任務的處理 |
onProgressUpdate() | 這個方法是在調用 publishProgress 的時候被調用的,是運行在 UI 線程的 |
onPostExecute() | 這個方法是任務執行完畢以後被調用的,是運行在 UI 線程中的 |
每一個狀態在一個任務的生命週期中只會被執行一次。bash
狀態 | 描述 |
---|---|
PENDING | 等待(尚未開始執行任務) |
RUNNING | 執行中 |
FINSHED | 完成 |
AsyncTask 的對象調用 execute 方法,execute 內部又調用了 executeOnExecutor ,onPreExecute 方法就是在這裏被回調,以後將 AsyncTask 的參數封裝成一個併發類,而後將其添加到排隊線程池(SerialExecutor)中進行排隊,若是當前有任務正在執行,則等待,不然 THREAD_POOL_EXECUTOR 執行該任務。在任務的執行過程當中,經過 InternalHandler 將進度 pos(MESSAGE_POST_GROGRESS)發送到主線程中,此時會調用 onProgressUpdate 方法,任務執行完畢以後,InternalHandler 將結果 post(MESSAGE_POST_RESULT) 發送到主線程中,此時 onPostExecute 或者 onCancle 會被調用,任務執行到這裏就結束了。多線程
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("取消任務%s\n",new Date().toString()));
}
});
}
private ProgressAsycn progressAsycn1;
private class ProgressAsycn extends AsyncTask<Integer,Integer,String> {
// 這個方法是在啓動以前被調用的,執行在 UI 線程中,在這裏咱們作一些任務啓動前的準備
@Override
protected void onPreExecute() {
super.onPreExecute();
mText.append(String.format("準備執行%s\n",new Date().toString()));
}
// 這個方法是執行在新的線程的中的
@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 "任務已經執行完畢";
}
// 這個方法是在調用 publishProgress 的時候被調用的,是運行在 UI 線程的
@Override
protected void onProgressUpdate(Integer... values) {
mText.append(String.format("工做進度:%d\n",values[0]));
}
// 這個方法是任務執行完畢以後被調用的
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
mText.append(String.format("任務執行完畢%s\n",new Date().toString()));
}
/**
* 異步任務被取消時回調,即 AsyncTask 的對象調用了 cancel 方法
* 這個方法和 onPostExecute 互斥
* doInBackground 方法中的任務執行完畢,纔會被回調
*/
@Override
protected void onCancelled() {
mText.append(String.format("異步任務已取消%s\n",new Date().toString()));
}
}
}
複製代碼
執行結果併發
java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
複製代碼
問題:從上面的執行結果中,咱們能夠看出兩個問題app
咱們在閱讀源碼以前,先給本身提一些問題,而後咱們在閱讀源碼的時候,帶着問題來去找答案,這樣咱們的目標纔會更加明確。異步
首先,咱們查看在 AsyncTask 的構造函數裏面到底作了些什麼async
/**
* 該構造方法必須在 UI 線程中被調用
*/
public AsyncTask() {
this((Looper) null);
}
/**
* 該構造方法必須在 UI 線程中被調用
*
* @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 的調用時機
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 作了如下幾個工做:ide
初始化 handler函數
private static InternalHandler sHandler;
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
複製代碼
初始化 mWorker
初始化 mFuture
接着若是想要啓動某一個任務,就須要調用該任務的 execute() 方法,所以如今咱們來看一看 execute() 方法的源碼
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
複製代碼
execute() 方法的比較簡單,只是調用了 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;
}
複製代碼
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 最終調用的是 SerialExecutor 中 execute() 方法。而 execute() 這個方法的邏輯是在子線程中執行的,而 execute 這個方法傳入的 Runnable 正是 mFuture,在 run 方法中調用了, mFuture 對象的 run。咱們再找到 FutureTask 中實現的 run 方法的代碼,代碼以下(有省略):
public void run() {
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
try {
result = c.call();
} catch (Throwable ex) {
}
}
} finally {
// .....
}
}
複製代碼
從上面的代碼中咱們能夠看出,最終調用了 Callable 中的 call (),而這個 callball 就是咱們在 executeOnExecutor 中傳入 mFuture 的 mWorker 對象。如今咱們又從新拿出 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() 方法的調用,並將結果給到 result,最後在方法結束以前調用了 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 對象發送了一條消息,消息中攜帶了 MESSAGE_POST_RESULT 常量和一個表示任務執行結果的 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 消息,就會去執行 finish() 方法,若是這是一條 MESSAGE_POST_PROGRESS 消息,就會去執行 onProgressUpdate() 方法。那麼 finish() 方法的源碼以下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製代碼
能夠看到,若是當前任務被取消掉了,就會調用 onCancelled() 方法,若是沒有被取消,則調用 onPostExecute() 方法,這樣當前任務的執行就所有結束了。
咱們注意到,在剛纔 InternalHandler 的handleMessage() 方法裏,還有一種 MESSAGE_POST_PROGRESS 的消息類型,這種消息是用於當前進度的,調用的正是 onProgressUpdate() 方法,那麼何時纔會發出這樣一條消息呢?相信你已經猜到了,查看publishProgress()方法的源碼,以下所示:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
複製代碼
最後咱們來理一下 executeOnExecutor 中的任務啓動到結束,關鍵方法的調用順序:
executeOnExecutor() -> sDefaultExecutor.execute() -> mFuture.run() -> mWorker.call() -> doInBackground() -> postResult() -> sHandler.sendMessage() -> sHandler.handleMessage() -> onPostExecute()
複製代碼
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 是一個靜態的 Handler 對象,爲了可以將執行環境切換到主線程中,這就要求 sHandler 必須在主線程中建立,也便是 AsyncTask 必須在主線程中建立。因爲靜態成員會在加載類的時候進行初始化,所以也要求 AsyncTask 的類必須在主線程中加載,不然 AsyncTask 沒法正常工做。
AsyncTask 的對象爲何必須在主線程中建立
由於在 AsyncTask 的構造函數中對 handler 進行了初始化的操做,因此 AsyncTask 必須在主線程中進行建立,不然 AsyncTask 沒法進行線程切換的工做
AsyncTask 是串行執行任務仍是並行執行任務?
在 Android 1.6 以前,AsyncTask 是串行執行任務的,Android 1.6的時候 AsyncTask 是並行執行任務的,Android 3.0 以後,爲了不併行錯誤,AsyncTask 又採用一個線程來串行執行任務。
AsyncTask 調用 cancel() 任務是否當即中止執行?onPostExecute() 還會被調用嗎?onCancelled() 何時被調用?
任務不會當即中止的,咱們調用 cancel 的時候,只是將 AsyncTask 設置爲 canceled(可取消)狀態,咱們從如下代碼能夠看出,AsyncTask 設置爲已取消的狀態,那麼以後 onProgressUpdate 和 onPostExecute 都不會被調用,而是調用了 onCancelled() 方法。onCancelled() 方法是在異步任務結束的時候才調用的。時機是和 onPostExecute 方法同樣的,只是這兩個方法是互斥的,不能同時出現。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
// 線程執行完以後纔會被調用
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
複製代碼
- (1)AsyncTask 的類必須在主線程加載
- (2)AsyncTask 的對象必須在主線程中建立
- (3)execute 方法必須在 UI 線程中調用
- (4)不要在主線程中調用 onPreExecute 、doInbackground、onPressUpdate、onPostExecute
- (5)AsyncTask 的對象只能被調用一次,不然會出錯
- (6)AsyncTask 不太適合作太耗時的操做
- (7)在 Android 1.6 以前,AsyncTask 是串行執行任務的,Android 1.6的時候 AsyncTask 是並行執行任務的,Android 3.0 以後,爲了不併行錯誤,AsyncTask 又採用一個線程來串行執行任務。
- (8)若是在 AsyncTask 中的 doInBackGround 中開啓了新的線程,咱們執行了 cancle() 方法來中止異步任務,線程是不會被中止的,直到任務執行完成爲止,這個過程當中,onProgressUpdate 和 onPostExecute 是不會被調用的