安卓 - 源碼 - AsyncTask

簡介

AsyncTask 是一個 Thread 和 Handler 的助手類。
通過該類,可以輕鬆的創建 需要把結果發佈到UI線程的短期後臺操作。
(長時間後臺操作推選使用 java.util.concurrent 的API)

在API 11前,線程池是一個普通的ThreadPoolExecutor,支持併發

new ThreadPoolExecutor(5, 128, 10, TimeUnit.SECONDS, 
          LinkedBlockingQueue<Runnable>(10), ThreadFactory);

在API 11後,線程池是一個維護了Runnable隊列的SerialExecutor,支持串行

new SerialExecutor();

注意到的是,SerialExecutor內部實際是通過一個支持併發的線程池工作的:

public static final Executor THREAD_POOL_EXECUTOR;
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
              [2,4], CPU*2+1, 30, TimeUnit.SECONDS,
              LinkedBlockingQueue<Runnable>(128), ThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

而AsyncTask支持通過executeOnExecutor執行在指定的線程池,所以如果需要執行併發任務的時候,可以直接使用

executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

而無需重新自定義線程池。


基本行爲

AsyncTask的行爲被定義爲2塊:

  • 泛型參數 <Params, Progress, Result>
    用於定義AsyncTask執行時使用的3種參數的類型:

    public abstract class AsyncTask<Params, Progress, Result> {...}
    
    Params   :執行時傳入的參數的類型;
    Progress :進度更新時傳出的參數的類型;
    Result   :執行完成時返回的參數的類型;
    
  • 執行步驟
    AsyncTask執行時主要的4個步驟方法:

    @MainThread
    protected void onPreExecute() {}
    :在後臺任務開始前的初始化方法,執行在UI線程,可以用於設置任務
    
    
    @WorkerThread
    protected abstract Result doInBackground(Params... params){...}
    :onPreExecute之後馬上執行,後臺任務的執行方法,也是AsyncTask的核心方法,在後臺線程中執行
    執行過程中,可以使用publishProgress通知後臺任務的進度更新
    執行結束時,返回的結果會作爲onPostExecute的參數
    
    
    @MainThread
    protected void onProgressUpdate(Progress... values) {...}
    :調用publishProgress後執行,執行在UI線程,用於執行後臺進度變更時需要變化的邏輯
    
    
    @MainThread
    protected void onPostExecute(Result result) {...}
    :後臺任務完成時調用,執行在UI線程,用於接收執行結果處理後臺任務執行完成的邏輯
    

工作原理

AsyncTask主要的工作可以概括成:

  1. 後臺耗時任務,AsyncTask的工作主體
  2. 在UI線程的任務進度通知以及任務完成通知

在AsyncTask的說明中提及,該類只是一個Thread和Handler的輔助類,而不是一個標準的線程操作框架:

AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
and does not constitute a generic threading framework.

其中涉及到的內容:

  1. Thread :後臺工作的所在線程,由預置或自定義線程池維護
  2. Handler:從後臺線程切換到UI線程的核心,完成所有後臺任務中對通知UI線程的通知操作

線程切換

AsyncTask中對Thread和Handler的處理切換,主要由以下的對象完成:

private final Handler mHandler;
:AsyncTask對象的成員,指向AsyncTask靜態成員sHandler


private static InternalHandler sHandler 
          = new InternalHandler(Looper.getMainLooper())
:傳入Looper.getMainLooper()說明運行在主線程
負責AsyncTask從工作線程切換到主線程,執行如onProgressUpdate等方法


private static class InternalHandler extends Handler {
      public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
            case MESSAGE_POST_RESULT:
                  result.mTask.finish(result.mData[0]);
                  break;
            case MESSAGE_POST_PROGRESS:
                  result.mTask.onProgressUpdate(result.mData);
                  break;
             }
       }
}
:AsyncTaskResult<?> result在下面分析
這裏主要關注sHandler的工作內容,sHandler處理的工作主要有:
1.MESSAGE_POST_RESULT   : 對應方法爲onPostExecute()
2.MESSAGE_POST_PROGRESS : 對應方法爲onProgressUpdate()
在該處可以推斷出:推送到sHandler的消息基本來自子線程,通過sHandler實現UI線程的切換


private static class AsyncTaskResult<Data> {
      final AsyncTask mTask;
      final Data[] mData;
      AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
      }
 }
:推送到sHandler的消息的數據封裝,主要封裝了當前的AsyncTask和操作參數
1.mTask:需要執行UI線程任務的AsyncTask對象
2.mData:AsyncTask執行UI線程任務的參數,在推送<Progress>參數時,可以是多參數的

以上的部分完成UI線程的工作任務,且反映了:
AsyncTask 的 onPostExecute() 和 onProgressUpdate() 運行在UI線程依賴的是Handler機制。


執行任務

  1. 從execute開始
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
      return executeOnExecutor(sDefaultExecutor, params);
}
:調用execute方法時,實際上調用了executeOnExecutor方法
傳入的線程池,則是上面簡介中提到的SerialExecutor,一個串行執行任務的線程池


@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {

      if (mStatus != Status.PENDING) ... ...
      :檢查AsyncTask實例的運行狀態
      AsyncTask每一個實例,只允許執行一次(execute)
      除了PENDING,其餘狀態都會拋出IllegalStateException


      mStatus = Status.RUNNING;
      onPreExecute();
      :修改爲運行中狀態,在UI線程中執行AsyncTask的預處理方法
       

      mWorker.mParams = params;
      exec.execute(mFuture);
      :這裏涉及到了一個mWorker對象,屬於後臺線程運行部分,下一部分說明
      大體上,mWorker是一個Callable,負責AsyncTask的後臺任務調用過程


      return this;
}
:executeOnExecutor大體上負責了2個內容:
1.判斷當前執行狀態
2.使用傳入的線程池,執行AsyncTask的後臺任務

  1. 執行後臺任務
    使用傳入的線程池執行AsyncTask的後臺任務,具體的執行流程,封裝在mWorker
private final WorkerRunnable<Params, Result> mWorker;
private static abstract class WorkerRunnable<Params, Result> 
      implements Callable<Result> {
      Params[] mParams;
}
:mWorker是 WorkerRunnable類的實例
本質上是一個保存了Params參數的Callable對象


mWorker = new WorkerRunnable<Params, Result>() {
      public Result call() throws Exception {

            mTaskInvoked.set(true);
            :設置調用狀態爲已被調用
            在該狀態設置前,doInBackground被標識爲未調用
            
            Result result = null;
            try {
                  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                  result = doInBackground(mParams);
                  :設置線程優先級爲後臺線程,執行doInBackground

            } catch (Throwable tr) {
                  mCancelled.set(true);
                  :當發生異常時,設置AsyncTask取消狀態爲已取消

                  throw tr;
            } finally {
                  postResult(result);
                  :發送執行結果,屬於執行完成的部分,後面介紹

            }

            return result;
      }
};
:mWorker負責處理了doInBackground的邏輯
以及doInBackground完成後調用postResult發送執行結果


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(..., e.getCause());
            } catch (CancellationException e) {
                  postResultIfNotInvoked(null);
            }
      }
};
private void postResultIfNotInvoked(Result result) {
      final boolean wasTaskInvoked = mTaskInvoked.get();
      if (!wasTaskInvoked) {
            postResult(result);
      }
}
:mFuture是包裹mWorker的FutureTask對象
這裏的邏輯基本上只有一種情況會發生,即 doInBackground拋出異常或錯誤,
導致 get() 方法調用時拋出 ExecutionException,然後 done() 方法拋出 RuntimeException


  1. 發送執行結果
private Handler getHandler() {
      return mHandler;
}
private Result postResult(Result result) {
      Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                  new AsyncTaskResult<Result>(this, result));
      message.sendToTarget();
      return result;
}
:把執行結果發送到mHandler,意味着在主線程執行


case MESSAGE_POST_RESULT:
      result.mTask.finish(result.mData[0]);
      break;
:mHandler處理執行結果的邏輯,調用了mTask的finish方法


public final boolean isCancelled() {
      return mCancelled.get();
}
private void finish(Result result) {
      if (isCancelled()) {
            onCancelled(result);
      } else {
            onPostExecute(result);
      }
      mStatus = Status.FINISHED;
}
:根據執行狀態選擇需要調用的方法
1.onCancelled() 即任務被取消
2.onPostExecute() 即任務完成

mCancelled爲true的情況有2種
1.調用了AsyncTask的cancel()方法取消任務
2.doInBackground()的過程中拋出異常,導致任務被取消


@MainThread
protected void onCancelled(Result result) {
      onCancelled();
}  
@MainThread
protected void onCancelled() {
}
@MainThread
protected void onPostExecute(Result result) {
}
:執行結果的方法都可以根據需求重寫

以上就是AsyncTask的工作過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章