AsyncTask源碼解析

雖然關於AsyncTask的文章有很多,並且文章對AsyncTask褒貶不一,本文關注的是AsyncTask的實現原理,以及它的優缺點,至於它的好壞,請讀者自行辨別。

什麼是AsyncTask

AsyncTask是android框架爲開發者提供的一個輔助類(只是一個類,不是庫)。

AsyncTask有什麼用

AsynTask的出現主要是爲了解決主線程和異步線程之間的交互問題的,說到這大家是不是想到了Handler,子線程,線程池等一些列的東西?如果沒有,那說明你還是太嫩啊!
其實AsyncTask就是Handler,子線程,線程池等技術的封裝,爲的是讓android開發者處理異步更方便,現在的開源庫也是這個道理。

AsyncTask怎麼使用

AsyncTask的使用非常簡單,讀者可以自行查找相關資料,如:官方文檔

AsyncTask源碼分析

在執行異步任務的時候,我們首先會調用:

new mAsyncTask().execute();

因此,我們就從這裏下手!

首先讓我們來看看execute()方法中的邏輯:

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

是不是一看就驚了個呆,竟然只有一行代碼,有一行代碼不代表它簡單,接下來我們看看executeOnExecutor(sDefaultExecutor, params);的邏輯。

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

我們看到代碼中有一個if語句:

 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代表當前的運行狀態,如果一個我們的asynctask正在運行,這時我們再一次執行了excute方法,就會引發throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
,而如果我們的asynctask已經運行結束了,這是我們再一次運行了excute方法,就會觸發throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
異常,通過這段代碼,我們應該可以明白,爲什麼excute方法只能被執行一次。

接下來代碼把狀態設置爲 mStatus = Status.RUNNING;:這是因爲如果沒有異常,當然是正在執行啦。

接着我們就看到了我們非常熟悉的方法:onPreExecute(); (如果不熟悉,說明你還沒有掌握AsyncTask的應用,就先不要看源碼了,先學應用,一步一步來),這也說明了這個方法是首先被調用的,並且是在主線程中執行的。

接着往下看,出現了一行mWorker.mParams = params;這樣的代碼,params我們可以知道,就是我們excute(Params…param)方法中傳過來的參數,可是mWorker又是什麼呢?我們點進去看看:

private final WorkerRunnable<Params, Result> mWorker;

發現這是一個WorkerRunnable對象,那WorkerRunnable對象又是啥玩意,我們再點進去:

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

額,原來實現了Callable接口,其中Callable接口中有一個call()方法。

我們再來看看mWorker是怎麼進行實例化的:

 mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

可以發現call()方法返回了Result,那Result從哪裏來呢,唉 唉 唉 !!!,好像又發現了一個熟悉的方法:Result result = doInBackground(mParams);,不過據說這個方法是在子線程中執行的,在這也沒見開子線程啊,這是怎麼回事?這個等會再說。

我們接着向下分析,執行到了exec.execute(mFuture);這行代碼,此時出現了個mFuture,這是什麼東西呢?

private final FutureTask<Result> mFuture;

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

它就相當於一個線程任務(Runnable);

exec是什麼東西呢?
executeOnExecutor(sDefaultExecutor, params);中傳過來的參數,sDefaultExecutor是啥呢?對,點進去看看:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

額,原來是一個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);
            }
        }
    }

其實這就是一個線程池,線程池中的任務就是mFuture:當傳入一個任務後,execute方法會首先把該任務添加到mTasks中,

if (mActive == null) {
                scheduleNext();
            }

這段代碼表示,如果當前沒有正在活動的任務,就調用scheduleNext()執行下一個AsyncTask任務,並且當一個任務執行完後,會繼續調用下一個任務進行執行,直到執行完所有的任務爲止,從這一點也可以看出AsyncTask是串行的。

其實在AsyncTask中有兩個線程池,一個是SerialExecutor,另一個是THREAD_POOL_EXECUTOR,前者用於任務的排隊,後者用於任務的執行。

還記得在上面我們遺留了一個問題,就是從哪裏可以看出來doInBackground(mParams)是在子線程中執行?我們再來分析一下這段代碼:

 mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

在此看到這些代碼,是不是悟到了什麼?doInBackground(mParams)在mWorker中,mWorker傳給了mFuture,mFuture又加入到了線程池中,接着返回了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發送消息,它發送了一個MESSAGE_POST_RESULT消息,接下來我們再看看它發送到的Handler是怎麼對消息進行處理的:

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

        @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是靜態的,並且Handler必須在主線程中進行創建,靜態成員是在類進行加載的時候進行初始化的,這也間接說明了AsyncTask必須在主線程中進行加載,handler收到MESSAGE_POST_RESULT消息後,會執行result.mTask.finish(result.mData[0])方法:

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

此邏輯表示,如果我們取消了任務,就不再執行onPostExecute(result)方法,否則,就把結果傳給onPostExecute(result).

OK,到此爲止,我們平時用的三個方法在源碼中都見到了,但是需要注意的是對於AsyncTask來說,google在不同的版本中改動是比較頻繁的,本文用的是API23,對於其他的版本請讀者按照同樣的分析方式自行分析即可。

當然,AsyncTask有很多的缺點,但是根據使用場景的不同,我們可以想辦法避免這些缺點,對於AsyncTask的一些缺點,請參考Android中糟糕的AsyncTask

如文章有錯誤,請留言告知

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