Android AsyncTask詳解

前言

一提到多線程,我們不得不提到AsyncTask,很多Android開發人員在網絡請求這塊,一般會使用開源框架,比如Volley,Okhttp,retrofit等。但是有一部分人,比較忠於封裝AsyncTask去實現。我們都知道AsyncTask內部是Handler實現的,今天我們就來一探究竟。


基本使用

AsyncTask使用了模板方法模式,使用AsyncTask,我們需要寫一個類去繼承它,然後重寫它的幾個方法,如下:

public class MyAsyncTask extends AsyncTask<String, Integer, String> {

    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();
    }

    @Override
    protected String doInBackground(String... params) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        // TODO Auto-generated method stub
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(String result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
    }
}

AsyncTask傳入的參數分別代表:在調用excute方法傳入的參數類型,onProgressUpdate方法的參數類型和doInBackground返回的參數類型。下面再說明下幾個方法:

a.onPreExecute:最先執行的方法,運行在主線程,一般我們做顯示Dialog等的準備工作。

b.doInBackground:在onPreExecute只後執行的方法,運行在線程池,這裏主要做耗時操作,然後將結果返回。

c.onPostExecute:最後執行的方法,運行在主線程,方法參數爲doInBackground返回的值。

d.onProgressUpdate:顯示請求任務進度的方法,運行在主線程,需要在doInBackground調用publishProgress(values)方法。比如說下載任務進度。。。

在使用的時候就直接:

new MyAsyncTask().excute();//當然也可以傳入一些參數

基本使用就差不多介紹完畢,下面我們羅列下AsyncTask需要注意的地方:

  1. AsyncTask必須在主線程中加載。不過在android4.1以上系統自動完成了。AsyncTask必須在主線程中創建。excute方法必須在主線程中調用。
  2. 一個AsyncTask對象只能執行一次,也就是隻能調用一次excute方法,否則會報異常。
  3. Android1.6之前AsyncTask是串行執行任務。Android1.6到Android3.0是並行執行任務。Android3.0以後是串行執行任務,不過我們可以通過自定義線程池,調用executeOnExecutor()方法,將自定義的線程傳入,然後並行處理。
  4. 不要顯示去調用onPreExecute等方法。

源碼分析

基本使用介紹完畢後,我們來看下AsyncTask的內部實現,先從我們的調用方法excute開始:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return 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;
    }

這邊分析的源碼是android4.4,默認傳入線程池是串行去處理的。代碼3行開始,我們可以看到這裏有個狀態判斷。總共有3個狀態:Status.PENDING,Status.RUNNING,Status.FINISHED;執行前,執行中,執行完。代碼17行我們就能看到onPreExecute()方法是最先執行的。20行exec.execute(mFuture);,線程池開始工作:

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

這裏出現兩個線程池:SerialExecutor 和THREAD_POOL_EXECUTOR。從代碼中我們可以看出:SerialExecutor主要是做任務的排隊,而THREAD_POOL_EXECUTOR纔是真正的做後臺工作。

在AsyncTask中就初始化了兩個對象mWorker和mFuture,這個在executeOnExecutor方法裏就應用到。而上述代碼第9行,調用mFuture.run方法,就保證了mWorker在線程池中調用call方法:

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

進入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:

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

我們可以看到InternalHandler 是一個靜態Handler對象,爲了能夠將環境最終切換到主線程,那麼InternalHandler就必須在主線程中。由於靜態成員會在加載類的時候初始化,那麼就變相的要求AsyncTask必須在主線程中加載了。這也印證了我們在前面提出的觀點。

然後,這裏對消息的類型進行了判斷,如果這是一條MESSAGE_POST_RESULT消息,就會去執行finish()方法,如果這是一條MESSAGE_POST_PROGRESS消息,就會去執行onProgressUpdate()方法。進入finish():

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

可以看到,如果當前任務被取消掉了,就會調用onCancelled()方法,如果沒有被取消,則調用onPostExecute()方法,這樣當前任務的執行就全部結束了。

上述有一個消息MESSAGE_POST_PROGRESS處理,然後調用onProgressUpdate,發消息的地方,我們應該能想到是:publishProgress方法,如下:

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

至此,我們相信我們對於AsyncTask的工作原理及注意事項已經理解的很透徹了。

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