Android的AsyncTask非同步任務淺析

YuanchaoLi發表於2019-03-27

Android的AsyncTask非同步任務淺析

實現原理

內部封裝了2個執行緒池+1個Handler(InternalHandler),1個執行緒池SerialExecutor任務排隊,一個執行緒池THREAD_POOL_EXECUTOR執行任務。

常用重寫的方法

  • onPreExecute:執行在主執行緒中,可做UI更新,顯示進度條通知等。
  • doInBackground:在子執行緒執行任務,接收的引數型別為AsyncTask第一個泛型,返回給onPostExecute的引數型別為AsyncTask第三個泛型。
  • onProgressUpdate:執行在主執行緒中,doInBackground中呼叫publishProgress方法後即可回撥到該方法中,用於UI進度更新。接收的引數型別為AsyncTask第二個泛型。
  • onPostExecute:執行在主執行緒中,doInBackground任務執行完畢後,就會回撥到該方法中。
  • onCancelled:呼叫AsyncTask的cancel方法時,會回撥到該方法中,內部呼叫Thread的interrupt方法,告訴執行緒池要取消任務,Thread在合適時機取消任務。
public class MyTask extends AsyncTask<String, Double, Float> {

    @Override // 子執行緒執行任務
    protected Float doInBackground(String... strings) {
        publishProgress(5D);
        return 1f;
    }

    @Override // 準備執行doInBackground任務時回撥
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override // doInBackground任務執行結束後回撥,接收的引數為doInBackground返回的值
    protected void onPostExecute(Float aFloat) {
        super.onPostExecute(aFloat);
    }

    @Override // doInBackground呼叫publishProgress會回撥到該方法中
    protected void onProgressUpdate(Double... values) {
        super.onProgressUpdate(values);
    }

    @Override // 呼叫AsyncTask的任務關閉後回撥
    protected void onCancelled() {
        super.onCancelled();
    }
}
複製程式碼

注意事項

  1. Android4.1之前,AsyncTask類必須在主執行緒中載入。Android4.1之後,沒有了這個要求,ActivityThread的main方法中自動載入了AsyncTask。
  2. AsyncTask物件要在主執行緒建立,建立時,AsyncTask構造方法中,會拿到當前執行緒的Looper,傳給new的Handler例項,以保證Handler是在主執行緒中。
public AsyncTask(@Nullable Looper callbackLooper) {
        // 建立AsyncTask物件時,若不傳外部的Handler例項,會走到這個構造中
        // 拿到當前執行緒的Looper,傳給new的Handler,則Handler在當前建立AsyncTask物件的執行緒中
        // 因此,若要保證Handler執行環境在主執行緒,必須要在主執行緒中建立AsyncTask物件
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
            
        ......省略無關程式碼
    }
複製程式碼
  1. AsyncTask物件的execute方法必須在主執行緒中呼叫。execute方法有@MainThread註解。
  2. Android3.0之前AsyncTask調execute方法是並行執行任務,3.0之後增加了SerialExecutor執行緒池,預設選擇該執行緒池序列執行任務,若想並行執行,則直接調executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)方法即可。
MyTask myTask = new MyTask();
// 預設在SerialExecutor執行緒池中序列執行
myTask.execute("1");
// 並行執行
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "1");
複製程式碼
  1. 一個AsyncTask物件,只能執行一次execute方法,第二次執行就會拋異常。
@MainThread // 主執行緒中執行的方法,預設選擇SerialExecutor執行緒池序列執行任務
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread  // 主執行緒中執行的方法。外部也可直接調這個方法執行任務,自己傳入執行緒池選擇是序列還是並行。
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 一個AsyncTask物件,若多次執行execute,走到這裡,會判斷任務在執行中或已結束時,都將拋異常
    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;
}
複製程式碼
  1. 記憶體洩漏問題。採用靜態的AsyncTask繼承類,若要引用外部,採用弱引用。
  2. 使用多個非同步操作並需要進行UI變更時,用AsyncTask會很複雜,替換Handler會更靈活。

相關文章