Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 深入分析AsyncTask

深入分析AsyncTask

編輯:關於Android編程

1. 什麼是AsyncTask

AsyncTask 即 asynchronous task,異步任務。

AsyncTask實際上是圍繞Thread和Handler設計的一個輔助類,在內部是對Thread和Handler的一種封裝。AsyncTask的異步體現在由後台線程進行運算(訪問網絡等比較耗時的操作),然後將結果發布到用戶界面上來更新UI,使用AsyncTask使得我不用操作Thread和Handler。

2. AsyncTask的簡單使用

new AsyncTask(){
    //// 運行在主線程中,做預備工作/////
    onPreExecute(){

    }
    // 運行在子線程中,做耗時操作
    String doingBackGround(String s){

    }
    // 運行在主線程中,耗時操作完成,更新UI
    onPostExecute(String s){

    }

}.execute(String);

AsyncTask用法比較簡單,Google設計這個類就是為了方便我們進行類似Handler這樣的異步操作。

如上代碼,一般使用AsyncTask只要重寫裡面的三個方法,onPreExecute和onPostExecute不是抽象方法,不是必須實現,實現這兩種方法一般能讓代碼的邏輯更加清晰。onPreExecute運行在主線程中,做一些准備工作。onPostExecute同樣運行在主線程中,用於在耗時操作完成後,更新UI。另外,還有一個onProgressUpdate方法,用於在後台任務執行過程中來實時地更新UI。
doingBackGround則是抽象方法,必須實現,我們使用AsyncTask時希望將一些耗時操作放在子線程中,doingBackGround中邏輯就相當於我們在Thread-Handler中Thread中的run方法中實現的邏輯。
execute方法用於啟動執行任務

3. 從源碼角度看AsyncTask的設計

接下來進入這篇文章的重點,我們從源碼角度來分析AsyncTask是如何實現的。

AsyncTask的execute方法

public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
上面是execute方法,發現execute其實內部調用的executeOnExecutor方法,調用executeOnExecutor方法傳遞了兩個參數,這裡第一個傳遞了一個默認的執行器,關於這個sDefaultExecutor我們再來講,我們現在來看AsyncTask的流程設計。 我們來看這個executeOnExecutor方法,這裡將不重要的代碼略去了,其實這裡面的邏輯比較清晰。
 public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
        .....
        .....

        mStatus = Status.RUNNING;
        ////////////////////////////////
        // 第一步:在主線程中執行准備操作////
        onPreExecute();
        // 第二步:把params參數賦值給mWorker
        mWorker.mParams = params;
        // 第三步:用線程池執行mFuture
        exec.execute(mFuture);
        ///////////////////////
        return this;
    }
第一步,onPreExecute與上面的介紹一樣,進行准備工作,這個就沒有必要分析,如果我們沒有重寫,就不會做相關的准備。我們主要第二步和第三步,第二步,把params參數賦值給mWorker,params是execute中傳遞過來的參數,同時也是泛型中第一個參數,將params賦值給mWorker,那麼mWorker是什麼呢?

AsyncTask的構造方法

我們繼續看源碼:主意到mWorker是在AsyncTask的構造方法中創建的。

public AsyncTask() {
        mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
               .....
            }
        };

        mFuture = new FutureTask(mWorker) {
            @Override
            protected void done() {
               .....
            }
        };
    }
首先,mWorker在構造方法中創建,它是一個匿名內部類,那WorkerRunnable是個什麼東西呢?
 private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
    }
我們發現WorkerRunnable其實就是一個Callable,同時在execute方法中mFuture也在這裡創建了出來,這裡會將mWorker傳遞到FutureTask中去,那麼將mFuture傳進去做了什麼什麼操作呢?
 public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        //////這裡將mWorker傳遞進來,其實就是callable///
        /////////////
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }
FutureTask是java.util.concurrent,FutureTask繼承了Future,通過 Future 接口,可以嘗試取消尚未完成的任務,查詢任務已經完成還是取消了,以及提取(或等待)任務的結果值。 在AsyncTask的構造方法中,將mWorker傳進來,即將callable傳進來,因為mWorker就是callable。 這樣在上面的executeOnExecutor中的第三步中,==exec.execute(mFuture)== 用線程池來執行mFuture,其實就是執行mFuture中的run方法,我們來看FutureTask中的run方法:

FutureTask中的run方法

 public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                   //① 調用callable中的call方法,其實就是mWorker中的call方法
                   //並且將結果賦值給result
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    //② 調用自己內部的set方法設置結果
                    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);
        }
    }
在FutureTask中的run方法,我們需要關注兩個地方,第一個,就是上面代碼片段的①處,這裡調用了mWorker中的call方法,這樣我們再回頭來看mWorker中的call方法。
mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };
 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }
在mWorker中call方法中主要就是執行耗時操作,正是doInBackground方法,並且將執行的結果result返回回去,用postResult對doInBackground進行包裹則是為了運用Handler機制來更新UI。 接下來我們看FutureTask中run方法中的②處,調用了FutureTask自己的set方法。
protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }
 private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        //① 調用了FutureTask中的done方法
        done();

        callable = null;        // to reduce footprint
    }
由set方法,調用finishCompletion,主要看finishCompletion的邏輯,我們只關注finishCompletion代碼的①處,這裡調用了done方法, 這樣我們來看done方法中的邏輯。
mFuture = new FutureTask(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);
                }
            }
        };
在Future中調用了postResultIfNotInvoked,其實這裡將這段處理邏輯抽取到方法中去了,在android2.3即以前的源碼都是沒有抽取的,這也是使得現在的邏輯更加清晰。 ==java.util.concurrent.atomic.AtomicBoolean ( 在這個Boolean值的變化的時候不允許在之間插入,保持操作的原子性==) 由於在mWorker中的call和在mFuture的done方法都會調用postResult來更新UI,由於是線程操作,不能保證先後順序,所以需要使用AtomicBoolean來保持操作的原子性。其實在2.3上的代碼不是這樣處理的,2.3上將更新UI的操作都放在mFuture中的done方法中。
private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }
最後,我們來看postResult方法。
  private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }
這裡就是我們非常熟悉的代碼了,使用Message發送消息給Handler來更新UI。 在AsyncTask中定義了一個InternalHandler,如果耗時操作執行完畢,就會執行finish(result.mData[0]),如果結果正在執行,則會onProgressUpdate來更新進度,這個onProgressUpdate正是我們前面說到的需要實現的更新進度的方法。
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;
            }
        }
    }
AsyncTaskResult類的只是一個封裝
  @SuppressWarnings({"RawUseOfParameterizedType"})
    private static class AsyncTaskResult {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            //////耗時操作執行完畢,更新UI///////////
            //onPostExecute運行在主線程
            onPostExecute(result);
            //////////////////////////////////////////
        }
        mStatus = Status.FINISHED;
    }
#

這樣我們關於AsyncTask的流程終於走通了,為什麼onPreExecute和onPostExecute運行在主線程,而doingBackGround為什麼運行在子線程中,這個邏輯是不是就變得清晰了。上面貼了好多代碼,一直都是在分析代碼的意思,至於關於設計的思想感覺現在的自己還體悟不夠。

4. AsyncTask在上面遺留的問題

關於AsyncTask中executeOnExecutor中的sDefaultExecutor

項目中問題場景:

操作步驟>>>>>>>>>>>
- 設置安全中,選擇指紋。
- 解鎖方式選擇圖案。
- 在選擇您的圖案界面,點擊確定,需要三到五秒才能跳轉到下一界面。

問題分析:在設置解鎖方式為為圖案時,第二次繪制圖案後,Settings源碼中使用了AsyncTask來將一些比較耗時的驗證操作放在子線程中去處理(參看下面的部分代碼),由於android原生設計是使用
AsyncTask中的一個參數的方法,一個參數的方法采用的是默認的執行器,即串行執行器

==frameworks/base/core/java/com/android/internal/widget/LockPatternChecker.java==

public static AsyncTask verifyPattern(final LockPatternUtils utils,
            final List pattern,
            final long challenge,
            final int userId,
            final OnVerifyCallback callback) {
        AsyncTask task = new AsyncTask() {
            private int mThrottleTimeout;
            @Override
            protected byte[] doInBackground(Void... args) {
                try {
                    return utils.verifyPattern(pattern, challenge, userId);
                } catch (RequestThrottledException ex) {
                    mThrottleTimeout = ex.getTimeoutMs();
                    return null;
                }
            }
            @Override
            protected void onPostExecute(byte[] result) {
                callback.onVerified(result, mThrottleTimeout);
            }
        };
        ////////////默認使用的串行的執行器//////////////
        task.execute();
        ///////////////////////////////////////////////
        return task;
    }
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
        final ArrayDeque mTasks = new ArrayDeque();
        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即是串行執行器,它的作用是保證任務執行的順序,也就是它可以保證提交的任務確實是按照先後順序執行的。它的內部有一個隊列用來保存所提交的任務,保證當前只運行一個,這樣就可以保證任務是完全按照順序執行的。如果發現異步任務還未執行,可能被我們發現SerialExecutor即是串行執行器順序的使用線程執行。因為應用中可能還有其他地方使用AsyncTask,所以到我們的AsyncTask也許會等待到其他任務都完成時才得以執行而不是調用executor()之後馬上執行。

如果executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,params)則不一樣。

 /**
     * 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);
API中的解釋:能夠並行的執行任務。THREAD_POOL_EXECUTOR是一個數量為corePoolSize的線程池,具體線程池的數量是依據CPU的核心來設置的,如果超過這個數量的線程個數就需要等待

SerialExecutor是按順序執行,THREAD_POOL_EXECUTOR則一定程度上能保證並行執行。

以上就是關於AsyncTask的全部內容,希望能對你有些幫助,貼了好多源碼,如果想真正弄清楚,還是得自己去閱讀閱讀源碼,整理這個也不容易,前前後後花了大概一個星期。
最後是AsyncTask的時序圖,畫的不太好,湊合看吧,O(∩_∩)O哈哈~
AsyncTask時序圖

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved