[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 會被調用,任務執行到這裏就結束了。markdown
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 的構造函數裏面到底作了些什麼異步
/** * 該構造方法必須在 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 作了如下幾個工做:async
初始化 handleride
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 是不會被調用的