Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android AsyncTask使用步驟與源碼分析

Android AsyncTask使用步驟與源碼分析

編輯:關於Android編程

AsyncTask的一個典型的應用場景是:後台下載文件,並實時跟新下載進度。它既能使得耗時的事情在後台線程中執行,又能和主線程通信,告訴主線程更新UI。同時,AsyncTask內部使用了線程池來執行後台任務,因此它能處理多任務請求。那麼它的內部是怎麼實現的呢?

使用步驟

在閱讀源碼之前,我們還是看一下AsyncTask的使用步驟:
這部分參考了AsyncTask的基本用法 這篇博客。

1.子類化AsyncTask

2.實現AsyncTask中定義的下面一個或幾個方法

2.1onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些准備工作,如在界面上顯示一個進度條。
2.2doInBackground(Params…), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
2.3onProgressUpdate(Progress…),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
2.4onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後台的計算結果將通過該方法傳遞到UI thread.

3.提交任務請求

使用executeOnExecutor後者execute方法。
同時,我們還需要注意一下幾點:
  A)Task的實例必須在UI thread中創建
  B) execute方法必須在UI thread中調用
  C) 不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)這幾個方法
  D) 該task只能被執行一次,否則多次調用時將會出現異常

源碼分析

AsyncTask的源碼不多,因此分析比較容易。我之所以要分析這個類的源碼是因為我遇到了這樣的困惑:既然AsyncTask使用線程池來處理多任務請求,那麼我提交多個任務是不是應該這樣呢?

        //1 創建一個AsyncTask 的實例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多個任務
        asyncTask.execute(1);
        asyncTask.execute(2);
        asyncTask.execute(3);
        asyncTask.execute(4);
        asyncTask.execute(5);

很不幸的是,程序立刻就掛掉了,問什麼呢?我想的,既然AsyncTask內部有個線程池,那麼我不斷給它提交任務不就可以了嗎?而事實證明這樣不行,那麼正確的姿勢是怎麼樣的呢?
比如要提交10個任務:

    for(int i=0;i<10;i++){
        //1 創建一個AsyncTask 的實例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多個任務
        asyncTask.execute(i);
    }

也就是說要不斷的new AsyncTask 的實例,這是什麼情況呢?難道不是每個AsyncTask都有一個線程池嗎?還真不是的。
打開源碼一看,立刻明白了,AsyncTask使用的是單例模式。

    /**
     * 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);

    /**
     * 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();

AsyncTask構造了兩個靜態的線程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR 。因為它們是靜態的,因此所有的AsyncTask對象都共享這兩個線程池。也就是說,我已開始的理解就是錯的,我以為一個AsyncTask內部有一個線程池,其實不然,而是所有的對象共享這兩個線程池,其中SERIAL_EXECUTOR 是默認使用的線程池,當然我們可以選擇使用THREAD_POOL_EXECUTOR作為線程池的,主要用到executeOnExecutor方法,比如:

asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,1);

理解了這點後,我們看看AsyncTask的具體內部實現。
當我們創建好AsyncTask實例後,采用默認的線程池的情況下,我們會執行excute方法,這個方法如下:

    @MainThread
    public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

調用executeOnExecutor方法進一步處理,注意傳給executeOnExecutor方法的參數sDefaultExecutor定義如下:

  private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

也就是說默認的線程池是SERIAL_EXECUTOR了。
然後我們看一下executeOnExecutor是如何進一步處理的:

    @MainThread
    public final AsyncTask 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;
    }

該方法中出現的Status定義如下:

    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

Status中只定義了三種狀態,因此,asyncTask的狀態如果不是Status.PENDING,就會拋出異常。這是為什麼我們向下面這樣使用程序會掛掉:

        //1 創建一個AsyncTask 的實例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多個任務
        asyncTask.execute(1);
        asyncTask.execute(2);
        asyncTask.execute(3);

第一次調用execute的時候,asyncTask的狀態由Status.PENDING轉為Status.RUNNING,第二次調用的是有,asyncTask的狀態還是Status.RUNNING,因此程序就會掛掉了。

step 1

接下來會調用onPreExecute()方法,也就是說這個方法是在UI線程中調用的,我們完全可以在其中更新UI。做一個初始化工作,這個時候,我們提交的任務還沒有執行。繼續往下看。

step 2

接下來調用了:

exec.execute(mFuture);

exec就是excute方法中傳入的sDefaultExecutor ,它默認初始化為SERIAL_EXECUTOR,SERIAL_EXECUTOR的定義如下:

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

exec.excute方法也就是這裡的execute方法了。這裡構造了一個Runnable的實例並把它添加到mTasks 中,第一次執行到這裡mActive 肯定為null,因此會調用scheduleNext方法,這個方法使用 mTasks.poll()去除之前添加的Runable,然後執行THREAD_POOL_EXECUTOR.execute(mActive),從而把Runable添加到線程池中。

step 3

Runnable中調用了傳入的Runnable的run方法,這個Runable就是mFuture,mFuture定義如下:

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

mFuture 是一個FutureTask的實例,並且這個實例接受一個mWork參數,這個mWork是一個和mFuture一樣頂一個AsyncTask的構造方法中:

        mWorker = new WorkerRunnable() {
            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);
            }
        };

mWorker 其實是一個Callable的實例了,他有個call方法。而mFuture本質上是一個Runnable的實例,調用mFuture的run方法其實就是調用FutureTask中的run方法,FutureTask接受了mWorker 作為參數,並把它賦值給自己的callable 屬性,代碼如下:

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

step 4

所以我們看看FutureTask的run方法

 public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable 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);
        }
    }

這個方法做了如下事情:
1.調用callable的call方法,也就是mWork的call方法,mWork的call方法step 3中已經貼過,其中會調用doInBackground方法,也就是我們提交的需要在後台執行的代碼了。因此,doInBackground是在mWork的call方法中被執行的,mWork的call方法又是通過THREAD_POOL_EXECUTOR.execute(mActive)被添加到線程池後被執行的。
2.調用postResult方法

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

postResult方法會發送一個消息,這個消息會使得InternalHandler的handleMessage方法被調用。這個時候,我們已經離開了後台線程,又回到UI線程了。因為InternalHandler的Looper是UI線程的Looper,其構造函數中有如下代碼可以知曉:

        public InternalHandler() {
            super(Looper.getMainLooper());
        }

注意我們獲取的消息為MESSAGE_POST_RESULT,因此根據handleMessage的定義:

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

會調用到AsyncTask的finish方法:

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

step 5

這個方法中,我們沒有調用取消方法的話,就會調用onPostExecute方法了,這樣,我們使用AsyncTask需要覆寫的幾個方法只有onProgressUpdate方法沒有調用了。那麼這個方法是怎麼被調用的呢?我們說,我們要更新進度的話,需要使用publishProgress方法,我們看看這個方法:

    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult

這個方法就是發送一個消息,然後還是InternalHandler方法handleMessage方法被調用,注意這會消息是MESSAGE_POST_PROGRESS,因此根據源碼,再貼一次handleMessage方法吧:

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

可見這次調用的是onProgressUpdate方法。至此,所有需要覆寫的方法的調用過程我們都分析結束了。

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