AsyncTask源碼淺析

在開發的過程中我們如果想進行一些耗時的操作不能直接在UI線程中進行,除了使用Handler機制來進行異步消息處理,Android還給我們提供了一個非常方便的類AsyncTask來進行異步操作,在這篇文章中只對AsyncTask的源碼進行一下梳理,如果對AsyncTask的使用還不太熟悉的可以先看一下API的使用方法。

我們往往是定義一個類繼承AsyncTask,然後創建出這個類的實例,而後調用execute方法來執行任務,我們就從這裏入手來分析源碼,首先是構造方法:

 /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

        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 occured while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

首先看一下代碼的註釋,告訴我們這個構造器必須在UI線程中進行調用,這個在後面會給出分析。構造方法很簡單,分別對創建出了WorkerRunnable和FutureTask對象並將其保存起來。先看看WorkerRunnable是什麼東西:

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

實現了Callable接口(可以簡單的將Callable對象看做一個執行run方法時有返回值的Runnable對象,只不過在Callable中改名叫call方法),是一個抽象類,在構造函數中重寫了call方法,暫時先不用考慮裏面的邏輯,後面會進行分析。

再看FutureTask類,它的構造方法中接受了一個Callable參數,也就是我們剛剛創建出來的WorkerRunnable對象,看一下它的方法列表不難找到一個比較熟悉的方法,run方法,我們進入看一下:

public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

只看一下關鍵的部分,在這個方法中調用了Callable對象的call方法。

構造函數已經簡單的分析完了,現在來看一下我們經常調用的execute方法:

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

可以看到,它只是簡單的調用了另一個方法,但是出現了一個沒有見過的參數sDefaultExecutor,來看一下這個參數是什麼:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

/**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    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);
            }
        }
    }

代碼不是很多,首先在線程池中維護了一個雙端隊列的實例,並且在線程池的execute方法中並沒有執行傳入的Runnable對象,而是將它用另一個新創建的Runnable對象包裝起來放到隊列的尾部。顯然,在第一次調用execute方法的時候mActive對象一定是null,這個時候會調用scheduleNext方法將隊列中的元素拿出來,並且使用THREAD_POOL_EXECUTOR來執行它,看一下THREAD_POOL_EXECUTOR的代碼:

 /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

只是創建了一個固定規格和模式的線程池,這裏不是分析的重點就不去深入它了。在執行完成後可以看到無論如何都會執行到SerialExecutor中execute方法的finally語句中,在這裏又調用了scheduleNext進行重複的工作。通過上面的分析也就不難理解註釋中說明的一次只能執行一個任務,雖然沒有繼續分析,但是可以猜想出AsyncTask的任務是由SerialExecutor進行分配,並且由於它是static的,在同一個進程內所有的AsyncTask中的任務都會放入到這個類的隊列中按順序執行。

接着回到AsyncTask的execute方法中,調用了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方法被調用了,由於前面的註釋中說過,AsyncTask類對象的創建必須是在主線程中執行的,所以onPreExecute方法的執行也是在主線程中的。而後將傳入的參數賦給了mWorker對象中的數組,使用傳入的線程池對mFuture進行執行,因爲傳入的參數是sDefaultExecutor,所以執行的邏輯跳轉回了SerialExecutor的execute方法,而後在線程池中執行了mFuture的run方法。根據我們前面的分析它的run方法調用了mWorker的call方法,那麼call方法是在哪裏實現的呢,沒錯,在構造函數中:

 public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }

在這裏執行了doInBackground方法,這個方法應該很熟悉了,它是一個抽象的方法,也是每次都要求我們必須進行重寫的。這裏由於執行過程還是在線程池中進行的,所以避免了在UI線程中進行耗時操作。方法執行完成後將返回值作爲參數傳遞給了postResult方法,進入看一下:

 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

在這裏面看到了很熟悉的代碼,Handler機制,那麼這個Handler是在哪創建的呢,

private static final InternalHandler sHandler = new InternalHandler();

private static class InternalHandler extends Handler {
        @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;
            }
        }
    }

很顯然Handler類是靜態的,在類加載的時候就會初始化這個類,現在回過頭再來想一下剛纔提到的AsyncTask一定要在UI線程中加載的問題,如果不在UI線程中進行加載,那麼Handler自然也就不在UI線程中,那麼onProgressUpdate方法和onPostExecute方法中也就不能執行更新UI的操作了。

繼續看postResult方法,很簡單,就是給Handler發了一個消息並且傳了一個參數,而Handler調用了AsyncTask的finish方法用到了這個參數,下面是finish方法:

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

在任務被取消時調用onCancelled方法回調,不然就調用我們熟悉的onPostExecute方法。而在Handler的handleMessage中還有一個接收項,它回調了我們常用的onProgressUpdate方法,找一下這個標記(MESSAGE_POST_PROGRESS)是怎樣傳入的,不難找到publishProgress方法:

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

這也正是我們在doInBackground中經常使用的。



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