Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> [安卓筆記]AsyncTask源碼剖析

[安卓筆記]AsyncTask源碼剖析

編輯:關於Android編程

前言: 初學AsyncTask時,就想研究下它的實現源碼,怎奈源碼看了好幾遍都沒看懂,於是擱置了。最近心血來潮,又看了一些源碼,如HandlerThread,IntentService,AsyncQueryHandler等,收獲頗深,於是乎想回頭再研究下AsyncTask,沒想到這次居然很容易看懂了。。。 正文: 注:1.讀者閱讀本文前,必須對android的Handler機制以及j.u.c中的線程池有所了解;2.AsyncTask使用方式不再贅述。3.不同版本AsyncTask內容有些不同.
首先明確AsyncTask是個抽象類,接受三個泛型參數,分表代表任務所需參數類型,任務進度類型,結果類型。
public abstract class AsyncTask 

開發者繼承AsyncTask後必須重寫doInbackground方法,其他方法如onPostExecute等按需重寫。
其內部有個靜態全局的線程池變量THREAD_POOL_EXECUTOR,AsyncTask的doInbackground中的任務就是由此線程池執行。
public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

ThreadPoolExecutor各參數含義如下,CORE_POOL_SIZE為核心線程數量,MAXIMUM_POOL_SIZE為線程池最大線程數量,KEEP_ALIVE參數表明當線程池的線程數量大於核心線程數量,那麼空閒時間超過KEEP_ALIVE時,超出部分的線程將被回收,sPoolWorkQueue是任務隊列,存儲的是一個個Runnable,sThreadFactory是線程工廠,用於創建線程池中的線程。所有這些參數在AsyncTask內部都已經定義好:
 private static final int CORE_POOL_SIZE = 5;
    private static final int MAXIMUM_POOL_SIZE = 128;
    private static final int KEEP_ALIVE = 1;
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);
        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
    private static final BlockingQueue sPoolWorkQueue =
            new LinkedBlockingQueue(10);

AsyncTask並不是直接使用上述線程池,而是進行了一層“包裝”,這個類就是SerialExecutor,這是個串行的線程池。
/**
     * 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 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);
            }
        }
    }

調用execute方法時,先將Runnable包裝一下(即加上try-finally塊)然後加入隊列,接著判斷當前mActive是否為空,第一次調用,此值為空,所以會調用scheduleNext方法,從隊列中取出頭部的任務,交給線程池THREAD_POOL_EXECUTOR處理,而處理過程是這樣的,先執行原先的Runnable的run方法,接著再執行scheduleNext從隊列中取出Runnable,如此循環,直到隊列為空,mActive重新為空為止。我們發現,經過這樣的處理,所有任務將串行執行所以我們需要注意,如果兩個AsyncTask都調用execute時,如果其中一個AsyncTask任務執行時間非常長,這將導致另一個AsyncTask的任務排隊等候,無法執行,因為它們共享同一個線程池且池是串行執行任務的。
接著看其他成員:
private static final int MESSAGE_POST_RESULT = 0x1;//當前消息類型--->任務完成消息
    private static final int MESSAGE_POST_PROGRESS = 0x2;//當前消息類型-->進度消息
    private static final InternalHandler sHandler = new InternalHandler();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;//默認線程池
    private final WorkerRunnable mWorker;
    private final FutureTask mFuture;
    private volatile Status mStatus = Status.PENDING;//當前狀態
    
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

sDefaultExecutor指明默認的線程池是串行池,mStatus指明當前任務狀態,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,//執行結束
    }

重點看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;
            }
        }
    }

InternalHandler繼承自Handler,並復寫了handleMessage,該方法根據消息類型做不同處理,如果任務完成,則調用finish方法,而finish方法根據任務狀態(取消or完成)調用onCancelled或者onPostExecute,這兩個是回調方法,通常我們會在onPostExecute中根據任務執行結果更新UI,這也是為什麼文檔中要求AsyncTask必須在UI線程中創建的原因,我們的Handler須與UI線程的Looper綁定才能更新UI,並且子線程默認是沒有Looper的。
 private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

如果是更新進度的消息(MESSAGE_POST_PROGRESS),那麼調用onProgressUpdate方法。
接下來我們關注的問題是doInbackground方法在何處被調用?聯想AsyncTask的使用,當我們創建好一個AsyncTask實例後,我們將調用其execute方法,所以,doInbackground方法應該在execute方法中執行,找到其實現:
 public final AsyncTask execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

並沒有直接調用doInbackground,而是調用了executeOnExecutor方法:
 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;
    }
executeOnExecutor方法的作用是使用指定的線程池執行任務,這裡當然使用的是sDefaultExecutor,也就是SerialExecutor,但是我們注意到這個方法是公共的,也就是說我們可以手動配置線程池,讓AsyncTask並行執行起來,最簡單的方法就是使用內部定義好的THREAD_POOL_EXECUTOR。 executeOnExecutor方法首先檢查當前狀態,如果不是Pending狀態則拋出異常,緊接著修改狀態為Running態,接著調用onPreExecute方法(預處理,相信大家不陌生)。
關鍵代碼是exec.execute(mFuture)這一句,這行代碼的作用是執行mFuture中定義好的任務,mFuture為FutureTask類型,FutureTask是Runnable的實現類(j.u.c中的),所以可以作為線程池execute方法的參數,我們找到其定義:
 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);
                }
            }
        };

我們都知道,FutureTask構造時必須傳入一個Callable的實現類,線程最終執行的是Callable的call方法(不明白的請看java線程並發庫),所以mWorker必然是Callable的實現類:
  private static abstract class WorkerRunnable implements Callable {
        Params[] mParams;
    }
 mWorker = new WorkerRunnable() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };

最終,我們在WorkRunnable方法中找到了doInBackground方法,歷經艱辛啊!! 剩下最後一個問題:消息是如何發送給Handler的? 1.任務執行完畢的消息是通過postResult方法發送的:
 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }

這個AsyncTaskResult封裝了數據域和對象本身:
 private static class AsyncTaskResult {
        final AsyncTask mTask;
        final Data[] mData;
        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
2.任務進度更新消息是通過publishProgress方法發送的:
 protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult
(this, values)).sendToTarget(); } }
至此,AsyncTask源碼分析完畢!

稍微做個總結1.AsyncTask是對線程池和Handler的封裝,但是默認情況下任務是串行執行的,需注意,多個AsyncTask實例共享同一線程池(線程池是static的); 2.高並發任務並不推薦使用AsyncTask,而應該改用線程池; 3.務必在主線程中創建AsyncTask,因為AsyncTask內部的Handler在創建時必須要綁定主線程的Looper; 4.只能在doInBackground方法中執行耗時任務,其他方法如onPreExecute、onPostExecute等運行於主線程上.


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