AsyncTask的原理解析

前言

AsyncTask是一個常用的已經封裝好的異步任務類,可以更加方便地執行後臺任務以及切換主線程去更新UI。從實現上來說,它封裝了Thread(線程池)和Handler

定義

一個抽象的泛型類,提供了Params, Progress, Result三個泛型參數。

  • a. Params:開始異步任務執行時傳入的參數類型,對應excute()中傳遞的參數
  • b. Progress:異步任務執行過程中執行進度值的類型
  • c. Result:異步任務執行完成後,返回的結果類型,與doInBackground()的返回值類型保持一致
public abstract class AsyncTask<Params, Progress, Result> {
}

核心方法

結合源碼的方式的方式看一下AsyncTask的一些核心方法

1.execute

手動調用開始執行異步任務,必須在UI線程中調用

   public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

然後內部調用了executeOnExecutor()方法

 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;
    }

首先調用了onPreExecute()方法,然後調用了exec.execute(mFuture)方法開始執行,這裏的exec是個串行的線程池sDefaultExecutor ,二話不說看定義。

 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
 public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
     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);
            }
        }
    }

AsyncTask中有兩個線程池SerialExecutor(用於任務的排隊)和THREAD_POOL_EXECUTOR(用於任務的執行)。

2.onPreExecute

回到onPreExecute方法,在主線程運行,異步任務運行之前執行,用於一些準備工作。可以選擇性重寫。

   @MainThread
    protected void onPreExecute() {
    }

下面看看AsyncTask的構造方法

   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);
                    //noinspection unchecked
                    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);
                }
            }
        };
    }

構造方法中對前面executeOnExecutor方法中提到的mWorkermFuture進行了初始化操作,然後執行了doInBackground()方法,而後又執行了postResult(result)方法。接下來分別講述這兩個方法。

3.doInBackground

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

抽象方法,必須重寫自定義線程任務,接受Params參數,返回Result結果,可以在裏面調用publishProgress()用於更新進度信息。

    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

會利用Handler發個消息,MESSAGE_POST_PROGRESS標誌更新UI,然後收到消息後會在主線程調用onProgressUpdate ()方法,完美實現了線程切換操作。

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        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;
            }
        }
    }

4.onProgressUpdate

在主線程中執行,後臺進度發生改變時調用,可以選擇重寫。

    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }

上述講述了執行完doInBackground()方法,而後又執行了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;
    }

同樣也是發送了消息,前面已經貼了Handler代碼,這次會調用result.mTask.finish(result.mData[0])方法。繼續跟

    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

邏輯很簡單,如果AsyncTask被取消執行了,就調用onCancelled(),否則調用onPostExecute(),將doInBackground()的返回結果傳遞給了onPostExecute()方法

5.onPostExecute

在主線程執行,返回doInBackground()的執行結果,將結果顯示到UI。

    @MainThread
    protected void onPostExecute(Result result) {
    }

6.onCancelled()

在主線程執行,當異步任務被取消時,onCancelled()會被調用,這個時候onPostExecute不會調用。

    @MainThread
    protected void onCancelled() {
    }

重要類

前面多次提到了這兩個類WorkerRunnableFutureTask

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

1.WorkerRunnable

 private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

這裏的Callable也是任務, 與Runnable的區別是Callable<T>存在返回值 。

2.FutureTask

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

其實就是一個任務包裝類,裏面包含Callable、增加了一些狀態標識 ,操作Callable的接口。

3.THREAD_POOL_EXECUTOR

在前面提到SerialExecutorscheduleNext()方法中調用了THREAD_POOL_EXECUTOR.execute(mActive)

/**
  * 源碼分析:THREAD_POOL_EXECUTOR.execute()
  * 說明:
  *     a. THREAD_POOL_EXECUTOR實際上是1個已配置好的可執行並行任務的線程池
  *     b. 調用THREAD_POOL_EXECUTOR.execute()實際上是調用線程池的execute()去執行具體耗時任務
  *     c. 而該耗時任務則是步驟2中初始化WorkerRunnable實例對象時複寫的call()
  * 注:下面先看任務執行線程池的線程配置過程,看完後請回到步驟2中的源碼分析call()
  */

    // 步驟1:參數設置
        //獲得當前CPU的核心數
        private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
        //設置線程池的核心線程數2-4之間,但是取決於CPU核數
        private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
        //設置線程池的最大線程數爲 CPU核數*2+1
        private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
        //設置線程池空閒線程存活時間30s
        private static final int KEEP_ALIVE_SECONDS = 30;

        //初始化線程工廠
        private static final ThreadFactory sThreadFactory = new ThreadFactory() {
            private final AtomicInteger mCount = new AtomicInteger(1);

            public Thread newThread(Runnable r) {
                return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
            }
        };

        //初始化存儲任務的隊列爲LinkedBlockingQueue 最大容量爲128
        private static final BlockingQueue<Runnable> sPoolWorkQueue =
                new LinkedBlockingQueue<Runnable>(128);

    // 步驟2: 根據參數配置執行任務線程池,即 THREAD_POOL_EXECUTOR
    public static final Executor THREAD_POOL_EXECUTOR;

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        // 設置核心線程池的 超時時間也爲30s
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

串行or並行

Android 1.6之前,AsyncTask是串行執行的,Android1.6之後採用線程池任務並行執行,從Android 3.0開始,AsyncTask又開始串行執行,就是一個接一個執行。當然也可以通過設置executeOnExecutor(Executor)來實現多個AsyncTask並行。

注意點

在使用AsyncTask時有一些問題需要注意的:

1 關於 生命週期

  • 結論
    AsyncTask不與任何組件綁定生命週期
  • 使用建議
    Activity 或 Fragment中使用 AsyncTask時,最好在Activity 或 FragmentonDestory()調用 cancel(boolean)

2 關於 內存泄漏

  • 結論
    AsyncTask被聲明爲Activity非靜態內部類,當Activity需銷燬時,會因AsyncTask保留對Activity的引用 而導致Activity無法被回收,最終引起內存泄露
  • 使用建議
    AsyncTask應被聲明爲Activity的靜態內部類

3 線程任務執行結果 丟失

  • 結論
    Activity重新創建時(屏幕旋轉 / Activity被意外銷燬時後恢復),之前運行的AsyncTask(非靜態的內部類)持有的之前Activity引用已無效,故複寫的onPostExecute()將不生效,即無法更新UI操作
  • 使用建議
    Activity恢復時的對應方法 重啓 任務線程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章