當前位置:
首頁 > 知識 > android 結合源碼深入剖析AsyncTask機制原理

android 結合源碼深入剖析AsyncTask機制原理

我們都知道,Android UI線程是不安全的,如果想要在子線程裡面進行UI操作,就需要直接Android的非同步消息處理機制,前面我寫了一篇從源碼層面分析了Android非同步消息Handler的處理機制。感興趣的可以去了解下。不過為了更方便我們在子線程中更新UI元素,Android1.5版本就引入了一個AsyncTask類,使用它就可以非常靈活方便地從子線程切換到UI線程。

一、基本用法

AsyncTask是一個抽象類,我們需要創建子類去繼承它,並且重寫一些方法。AsyncTask接受三個泛型的參數:

  • Params:指定傳給任務執行時的參數的類型
  • Progress:指定後台任務執行時將任務進度返回給UI線程的參數類型
  • Result:指定任務完成後返回的結果類型

除了指定泛型參數,還需要根據重寫一些方法,常用的如下:

onPreExecute():這個方法在UI線程調用,用於在任務執行器那做一些初始化操作,如在界面上顯示載入進度 空間onInBackground:在onPreExecute()結束之後立刻在後台線程調用,用於耗時操作。在這個方法中可調用publishProgress方法返回任務的執行進度。onProgressUpdate:在doInBackground調用publishProgress後被調用,工作在UI線程onPostExecute:後台任務結束後被調用,工作在UI線程。

二、AsyncTask類源碼解析

AsyncTask.java源碼地址

源碼注釋大致翻譯一下:

AsyncTask可以輕鬆的正確使用UI線程,該類允許你執行後台操作並在UI線程更新結果,從而避免了無需使用Handler來操作線程AsyncTask是圍繞Thread與Handler而設計的輔助類,所以它並不是一個通用的線程框架。理想情況下,AsyncTask應該用於短操作(最多幾秒)。如果你的需求是在長時間保持線程運行,強烈建議您使用由

java.util.concurrent提供的各種API包,比如Executor、ThreadPoolExecutor或者FutureTask。一個非同步任務被定義為:在後台線程上運行,並在UI線程更新結果。一個非同步任務通常由三個類型:Params、Progress和Result。以及4個步驟:onPreExecute、doInBackground、onProgressUpdate、onPostExecute。用法:AsyncTask必須由子類實現後才能使用,它的子類至少重寫doInBackground()這個方法,並且通常也會重寫onPostExecute()這個方法

取消任務

在任何時候都可以通過調用cancel(boolean)來取消任務。調用此方法將導致isCancelled()方法的後續調用返回true。調用此方法後,在執行doInBackground(Object [])方法後,將調用onCancelled(object),而不是onPostExecute(Object)方法。為了儘可能快的取消任務,如果可能的話,你應該在調用doInBackground(Object[])之前檢查isCancelled()的返回值。線程規則,這個類必須遵循有關線程的一些規則才能正常使用,規則如下:

  • 必須在UI線程上載入AsyncTask類,android4.1上自動完成
  • 任務實例必須在UI線程上實例化
  • execute()方法必須在主線程上調用
  • 不要手動去調用onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()方法。
  • 這個任務只能執行一次(如果嘗試第二次執行,將會拋出異常)。
  • 該任務只能執行一次(如果嘗試第二次執行,將拋出異常)。

內存的觀察AsyncTask。保證所有回調調用都是同步的,使得以下操作在沒有顯示同步情況下是安全的。

  • 在構造函數或者onPreExecute設置成員變數,並且在doInBackground()方法中引用它們。
  • 在doInBackground()設置成員欄位,並在onProgressUpdate()和onPostExecute()方法中引用他們。

執行順序。第一引入AsyncTask時,AsyncTasks是在單個後台線程串列執行的。在android1.6以後,這被更改為允許多個任務並行操作的線程池。從android 3.0開始,每個任務都是執行在一個獨立的線程上,這樣可以避免一些並行執行引起的常見的應用程序錯誤。如果你想要並行執行,可以使用THREAD_POOL_EXECUTOR來調用executeOnExecutor()方法。

(二)、AsyncTask的結構

AsyncTask的結構如下:

android 結合源碼深入剖析AsyncTask機制原理

我們看到在AsyncTask有4個自定義類,一個枚舉類,一個靜態塊,然後才是這個類的具體變數和屬性,那我們就依次講解;

(三)、枚舉Status

代碼在AsyncTask.java 256行

  1. public enum Status {
  2. /**
  3. * Indicates that the task has not been executed yet.
  4. */
  5. PENDING,
  6. /**
  7. * Indicates that the task is running.
  8. */
  9. RUNNING,
  10. /**
  11. * Indicates that {@link AsyncTask#onPostExecute} has finished.
  12. */
  13. FINISHED,
  14. }

枚舉Status上的注釋翻譯一下就是:Status表示當前任務的狀態,每種狀態只能在任務的生命周期內設置一次。

所以任務有三種狀態

  • PENDING:表示任務尚未執行的狀態
  • RUNNING:表示任務正在執行
  • FINISHED:任務已完成

(四)、私有的靜態類InternalHandler

代碼在AsyncTask.java 656行

  1. private static class InternalHandler extends Handler {
  2. public InternalHandler() {
  3. // 這個handler是關聯到主線程的
  4. super(Looper.getMainLooper());
  5. }
  6. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  7. @Override
  8. public void handleMessage(Message msg) {
  9. AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
  10. switch (msg.what) {
  11. case MESSAGE_POST_RESULT:
  12. // There is only one result
  13. result.mTask.finish(result.mData[0]);
  14. break;
  15. case MESSAGE_POST_PROGRESS:
  16. result.mTask.onProgressUpdate(result.mData);
  17. break;
  18. }
  19. }
  20. }

通過上面的代碼我們知道:

InternalHandler繼承自Handler,並且在它的構造函數裡面調用了Looper.getMainLooper(),所以我們知道這個Handler是關聯主線程的。重寫了handleMessage(Message)方法,其中這個Message的obj這個類型是AsyncTaskResult(AsyncTaskResult我將在下面講解),然後根據msg.what的來區別。我們知道這個Message只有兩個標示,一個是MESSAGE_POST_RESULT 是代表消息的結果,一個是MESSAGE_POST_PROGRESS代表要執行onProgressUpdate()方法。

通過這段代碼我們可以推測AsyncTask內部實現線程切換,即切換到主線程是通過Handler來實現的。

(五)、私有的靜態類AsyncTaskResult

代碼在AsyncTask.java 682行

  1. @SuppressWarnings({"RawUseOfParameterizedType"})
  2. private static class AsyncTaskResult<Data> {
  3. final AsyncTask mTask;
  4. final Data[] mData;
  5. AsyncTaskResult(AsyncTask task, Data... data) {
  6. mTask = task;
  7. mData = data;
  8. }
  9. }

通過類名,我們大概可以推測出這一個負責AsyncTask結果的類

AsyncTaskResult這個類 有兩個成員變數,一個是AsyncTask一個是泛型的數組。

  • mTask參數:是為了AsyncTask是方便在handler的handlerMessage回調中方便調用AsyncTask的本身回調函數,比如onPostExecute()函數、onPreogressUpdata()函數,所以在AsyncTaskResult需要持有AsyncTask。
  • mData參數:既然是代表結果,那麼肯定要有一個變數持有這個計算結果

(六)、私有靜態抽象類WorkerRunnable

代碼在AsyncTask.java 677行

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

這個抽象類很簡答,首先是實現了Callable介面,然后里面有個變數

mParams,類型是泛型傳進來的數組

(七)、局部變數詳解

AsyncTask的局部變數如下:

  1. private static final String LOG_TAG = "AsyncTask";
  2. private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
  3. // We want at least 2 threads and at most 4 threads in the core pool,
  4. // preferring to have 1 less than the CPU count to avoid saturating
  5. // the CPU with background work
  6. private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
  7. private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
  8. private static final int KEEP_ALIVE_SECONDS = 30;
  9. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  10. private final AtomicInteger mCount = new AtomicInteger(1);
  11. public Thread newThread(Runnable r) {
  12. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  13. }
  14. };
  15. private static final BlockingQueue<Runnable> sPoolWorkQueue =
  16. new LinkedBlockingQueue<Runnable>(128);
  17. /**
  18. * An {@link Executor} that can be used to execute tasks in parallel.
  19. */
  20. public static final Executor THREAD_POOL_EXECUTOR;
  21. /**
  22. * An {@link Executor} that executes tasks one at a time in serial
  23. * order. This serialization is global to a particular process.
  24. */
  25. public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
  26. private static final int MESSAGE_POST_RESULT = 0x1;
  27. private static final int MESSAGE_POST_PROGRESS = 0x2;
  28. private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
  29. private static InternalHandler sHandler;
  30. private final WorkerRunnable<Params, Result> mWorker;
  31. private final FutureTask<Result> mFuture;
  32. private volatile Status mStatus = Status.PENDING;
  33. private final AtomicBoolean mCancelled = new AtomicBoolean();
  34. private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

那我們就來一一解答

  1. String LOG_TAG = "AsyncTask":列印專用
  2. CPU_COUNT = Runtime.getRuntime().availableProcessors():獲取當前CPU的核心數
  3. CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)):線程池的核心容量,通過代碼我們知道是一個大於等於2小於等於4的數
  4. MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1:線程池的最大容量是2倍的CPU核心數+1
  5. KEEP_ALIVE_SECONDS = 30:過剩的空閑線程的存活時間,一般是30秒
  6. sThreadFactory:線程工廠,通過new Thread來獲取新線程,裡面通過使用AtomicInteger原子整數保證超高並發下可以正常工作。
  7. sPoolWorkQueue:靜態阻塞式隊列,用來存放待執行的任務,初始容量:128個
  8. THREAD_POOL_EXECUTOR:線程池
  9. SERIAL_EXECUTOR = new SerialExecutor():靜態串列任務執行器,其內部實現了串列控制,循環的取出一個個任務交給上述的並發線程池去執行。
  10. MESSAGE_POST_RESULT = 0x1:消息類型,代表發送結果
  11. MESSAGE_POST_PROGRESS = 0x2:消息類型,代表進度
  12. sDefaultExecutor = SERIAL_EXECUTOR:默認任務執行器,被賦值為串列任務執行器,就是因為它,AsyncTask變成了串列的了。
  13. sHandler:靜態Handler,用來發送上面的兩種通知,採用UI線程的Looper來處理消息,這就是為什麼AnsyncTask可以在UI線程更新UI
  14. WorkerRunnable<Params, Result> mWorke:是一個實現Callback的抽象類,擴展了Callable多了一個Params參數。
  15. mFuture:FutureTask對象
  16. mStatus = Status.PENDING:任務的狀態默認為掛起,即等待執行,其類型標示為volatile
  17. mCancelled = new AtomicBoolean():原子布爾類型,支持高並發訪問,標示任務是否被取消
  18. mTaskInvoked = new AtomicBoolean():原子布爾類型,支持高並發訪問,標示任務是否被執行過

(八)、靜態代碼塊

代碼在AsyncTask.java 226行

  1. static {
  2. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
  3. CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
  4. sPoolWorkQueue, sThreadFactory);
  5. threadPoolExecutor.allowCoreThreadTimeOut(true);
  6. THREAD_POOL_EXECUTOR = threadPoolExecutor;
  7. }

通過上面的(七)、局部變數詳解,我們知道在靜態代碼塊中創建了一個線程池threadPoolExecutor,並設置了核心線程會超時關閉,最後並把這個線程池指向THREAD_POOL_EXECUTOR。

(九)、私有的靜態類SerialExecutor

代碼在AsyncTask.java 226行

  1. private static class SerialExecutor implements Executor {
  2. // 循環數組實現的雙向Queue,大小是2的倍數,默認是16,有隊頭和隊尾巴兩個下標
  3. final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
  4. // 正在運行runnable
  5. Runnable mActive;
  6. public synchronized void execute(final Runnable r) {
  7. // 添加到雙向隊列中去
  8. mTasks.offer(new Runnable() {
  9. public void run() {
  10. try {
  11. //執行run方法
  12. r.run();
  13. } finally {
  14. //無論執行結果如何都會取出下一個任務執行
  15. scheduleNext();
  16. }
  17. }
  18. });
  19. // 如果沒有活動的runnable,則從雙端隊列裡面取出一個runnable放到線程池中運行
  20. // 第一個請求任務過來的時候mActive是空的
  21. if (mActive == null) {
  22. //取出下一個任務來
  23. scheduleNext();
  24. }
  25. }
  26. protected synchronized void scheduleNext() {
  27. //從雙端隊列中取出一個任務
  28. if ((mActive = mTasks.poll()) != null) {
  29. //線線程池執行取出來的任務,真正的執行任務
  30. THREAD_POOL_EXECUTOR.execute(mActive);
  31. }
  32. }
  33. }

首先,注意SerialExecutor的execute是synchronized的,所以無論多個少任務調用execute()都是同步的。其次,SerialExecutor裡面一個ArrayDeque隊列,通過代碼我們知道,SerialExecutor是通過ArrayDeque來管理Runnable對象的。通過上面我們知道execute()是同步的,所以如果你有10個任務同時調用SerialExecutor的execute()方法,就會把10個Runnable先後放入到mTasks中去,可見mTasks緩存了將要執行的Runnable。再次1,如果我們第一個執行execute()方法時,會調用ArrayDeque的offer()方法將傳入的Runnable對象添加到隊列的尾部,然後判斷mActive是不是null,因為是第一次調用,此時mActive還沒有賦值,所以mActive等於null。所以此時mActive == null成立,所以會調用scheduleNext()方法。再次2,我們在調用scheduleNext()裡面,看到會調用mTasks.poll(),我們知道這是從隊列中取出頭部元素,然後把這個頭部元素賦值給mActive,然後讓THREAD_POOL_EXECUTOR這個線程池去執行這個mActive的Runnable對象。再次3,如果這時候有第二個任務入隊,但是此時mActive!=null,不會執行scheduleNext(),所以如果第一個任務比較耗時,後面的任務都會進入隊列等待。再次4,上面知道由於第二個任務入隊後,由於mActive!=null,所以不會執行scheduleNext(),那樣這樣後面的任務豈不是永遠得不到處理,當然不是,因為在offer()方法裡面傳入一個Runnable的匿名類,並且在此使用了finnally代碼,意味著無論發生什麼情況,這個finnally裡面的代碼一定會執行,而finnally代碼塊裡面就是調用了scheduleNext()方法,所以說每當一個任務執行完畢後,下一個任務才會執行。最後,SerialExecutor其實模仿的是單一線程池的效果,如果我們快速地啟動了很多任務,同一時刻只會有一個線程正在執行,其餘的均處於等待狀態。

PS:scheduleNext()方法是synchronized,所以也是同步的

重點補充:

在Android 3.0 之前是並沒有SerialExecutor這個類的,那個時候是直接在AsyncTask中構建一個sExecutor常量,並對線程池總大小,同一時刻能夠運行的線程數做了規定,代碼如下:

  1. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
  2. MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

三、AsyncTask類的構造函數

代碼如下:

  1. public AsyncTask() {
  2. mWorker = new WorkerRunnable<Params, Result>() {
  3. public Result call() throws Exception {
  4. // 設置方法已經被調用
  5. mTaskInvoked.set(true);
  6. // 設定結果變數
  7. Result result = null;
  8. try {
  9. //設置線程優先順序
  10. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  11. //noinspection unchecked
  12. //執行任務
  13. result = doInBackground(mParams);
  14. Binder.flushPendingCommands();
  15. } catch (Throwable tr) {
  16. // 產生異常則設置失敗
  17. mCancelled.set(true);
  18. throw tr;
  19. } finally {
  20. // 無論執行成功還是出現異常,最後都會調用PostResult
  21. postResult(result);
  22. }
  23. return result;
  24. }
  25. };
  26. mFuture = new FutureTask<Result>(mWorker) {
  27. @Override
  28. protected void done() {
  29. try {
  30. // 就算沒有調用讓然去設置結果
  31. postResultIfNotInvoked(get());
  32. } catch (InterruptedException e) {
  33. android.util.Log.w(LOG_TAG, e);
  34. } catch (ExecutionException e) {
  35. throw new RuntimeException("An error occurred while executing doInBackground()",
  36. e.getCause());
  37. } catch (CancellationException e) {
  38. postResultIfNotInvoked(null);
  39. }
  40. }
  41. };
  42. }

通過注釋我們知道,這個方法創建一個非同步任務,構造函數必須在UI線程調用

構造函數也比較簡單,主要就是給mWorker和mFuture初始化,其中WorkerRunnable實現了Callable介面,

在構造函數裡面調用了postResult(Result)和postResultIfNotInvoked(Result),那我們就來分別看下

1、postResult(Result)方法

  1. // doInBackground執行完畢,發送消息
  2. private Result postResult(Result result) {
  3. @SuppressWarnings("unchecked")
  4. // 獲取一個Message對象
  5. Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
  6. new AsyncTaskResult<Result>(this, result));
  7. // 發送給線程
  8. message.sendToTarget();
  9. return result;
  10. }

通過代碼我們知道

  • 生成一個Message
  • 把這個Message發送出

這裡面調用了 getHandler(),那我們來看下這個方法是怎麼寫的

2、getHandler()方法

  1. private static Handler getHandler() {
  2. synchronized (AsyncTask.class) {
  3. if (sHandler == null) {
  4. sHandler = new InternalHandler();
  5. }
  6. return sHandler;
  7. }
  8. }

我們看到返回的是InternalHandler對象,上面說過了InternalHandler其實是關聯主線程的,所以上面方法 message.sendToTarget(); 其實是把消息發送給主線程。


大家注意一下 這裡的Message的what值為MESSAGE_POST_RESULT,我們來看下InternalHandler遇到InternalHandler這種消息是怎麼處理的

  1. private static class InternalHandler extends Handler {
  2. ...
  3. public void handleMessage(Message msg) {
  4. AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
  5. switch (msg.what) {
  6. case MESSAGE_POST_RESULT:
  7. // There is only one result
  8. result.mTask.finish(result.mData[0]);
  9. break;
  10. ...
  11. }
  12. }
  13. }

我們看到MESSAGE_POST_RESULT對應的是指是執行AsyncTask的finish(Result)方法,所以我們可以這樣說,無論AsyncTask是成功了還是失敗了,最後都會執行finish(Result)方法。那我們來看下finish(Result)方法裡面都幹了什麼?

2.1、finish(Result result) 方法

  1. private void finish(Result result) {
  2. if (isCancelled()) {
  3. // 如果消息取消了,執行onCancelled方法
  4. onCancelled(result);
  5. } else {
  6. // 如果消息沒有取消,則執行onPostExecute方法
  7. onPostExecute(result);
  8. }
  9. // 設置狀態值
  10. mStatus = Status.FINISHED;
  11. }

注釋寫的很清楚了,我這裡就不說明了,通過上面的代碼和finish方法的分析,我們知道無論成功還是失敗,最後一定會調用finish(Result)方法,所以最後狀態的值為FINISHED。

3、postResultIfNotInvoked(Result)方法

  1. private void postResultIfNotInvoked(Result result) {
  2. // 獲取mTaskInvoked的值
  3. final boolean wasTaskInvoked = mTaskInvoked.get();
  4. if (!wasTaskInvoked) {
  5. postResult(result);
  6. }
  7. }

通過上面代碼我們知道,如果mTaskInvoked不為true,則執行postResult,但是在mWorker初始化的時候為true,除非在沒有執行call方法時候,如果沒有執行call,說明這個非同步線程還沒有開始執行,這個時候mTaskInvoked為false。而這時候調用postResultIfNotInvoked則還是會執行postResult(Result),這樣保證了AsyncTask一定有返回值。

四、AsyncTask類核心方法解析

(一)、void onPreExecute()

  1. /**
  2. * Runs on the UI thread before {@link #doInBackground}.
  3. *
  4. * @see #onPostExecute
  5. * @see #doInBackground
  6. */
  7. // 在調用doInBackground()方法之前,跑在主線程上
  8. @MainThread
  9. protected void onPreExecute() {
  10. }

其實注釋很清楚了,在task任務開始執行的時候在主線程調用,在doInBackground(Params… params) 方法之前調用。

(二)、AsyncTask<Params, Progress, Result> onPreExecute() 方法

  1. @MainThread
  2. public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  3. return executeOnExecutor(sDefaultExecutor, params);
  4. }

首先來翻譯一下注釋

  • 使用指定的參數來執行任務,這個方法的返回值是this,也就是它自己,因為這樣設計的目的是可以保持對它的引用。
  • 注意:它的調度模式是不同的,一種是單個後台線程,一種是通過線程池來實現,具體那種模式是根據android版本的不同而不同,當最開始引入AsyncTask的時候,AsyncTask是單個後台線程上串列執行,從Android DONUT 開始,模式變更為通過線程池多任務並行執行。在Android HONEYCOMB開始,又變回了在單個線程上執行,這樣可以避免並行執行的錯誤。如果你還是想並行執行,你可以使用executeOnExecutor()方法並且第一個參數是THREAD_POOL_EXECUTOR就可以了,不過,請注意有關使用警告。
  • 必須在UI主線程上調用此方法。

通過代碼我們看到,它的內部其實是調用executeOnExecutor(Executor exec, Params... params)方法,只不過第一個參數傳入的是sDefaultExecutor,而sDefaultExecutor是SerialExecutor的對象。上面我們提到了SerialExecutor裡面利用ArrayDeque來實現串列的,所以我們可以推測出如果在executeOnExecutor(Executor exec, Params... params)方法裡面如果第一個參數是自定義的Executor,AsyncTask就可以實現並發執行。

(三)、executeOnExecutor(Executor exec, Params... params) 方法

  1. @MainThread
  2. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
  3. Params... params) {
  4. if (mStatus != Status.PENDING) {
  5. switch (mStatus) {
  6. case RUNNING:
  7. throw new IllegalStateException("Cannot execute task:"
  8. + " the task is already running.");
  9. case FINISHED:
  10. throw new IllegalStateException("Cannot execute task:"
  11. + " the task has already been executed "
  12. + "(a task can be executed only once)");
  13. }
  14. }
  15. //設置狀態
  16. mStatus = Status.RUNNING;
  17. 從這裡我們看出onPreExecute是先執行,並且在UI線程
  18. onPreExecute();
  19. // 設置參數
  20. mWorker.mParams = params;
  21. // 開啟了後台線程去計算,這是真正調用doInBackground的地方
  22. exec.execute(mFuture);
  23. // 接著會有onProgressUpdate會被調用,最後是onPostExecute
  24. return this;
  25. }

該方法首先是判斷mStatus狀態,如果是正在運行(RUNNING)或者已經結束(FINISHED),就會拋出異常。接著設置狀態為RUNNING,即運行,執行onPreExecute()方法,並把參數的值賦給mWorker.mParams於是Executor去執行execute的方法,學過Java多線程的都知道,這裡方法是開啟一個線程去執行mFuture的run()方法(由於mFuture用Callable構造,所以其實是執行的Callable的call()方法,而mWorker是Callable的是實現類,所以最終執行的是mWorker的call()方法)

PS:mFuture和mWorker都是在AsyncTask的構造方法中初始化過的。

主要是設置後台進度,onProgressUpdate會被調用

  1. @WorkerThread
  2. protected final void publishProgress(Progress... values) {
  3. if (!isCancelled()) {
  4. getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
  5. new AsyncTaskResult<Progress>(this, values)).sendToTarget();
  6. }
  7. }

這個方法內部實現很簡單

  • 首先判斷 任務是否已經被取消,如果已經被取消了,則什麼也不做
  • 如果任務沒有被取消,則通過InternalHandler發送一個what為MESSAGE_POST_PROGRESS的Message

這樣就進入了InternalHandler的handleMessage(Message)裡面了,而我們知道InternalHandler的Looper是Looper.getMainLooper(),所以處理Message是在主線程中,我們來看下代碼

  1. private static class InternalHandler extends Handler {
  2. public InternalHandler() {
  3. super(Looper.getMainLooper());
  4. }
  5. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  6. @Override
  7. public void handleMessage(Message msg) {
  8. AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
  9. switch (msg.what) {
  10. case MESSAGE_POST_RESULT:
  11. // There is only one result
  12. result.mTask.finish(result.mData[0]);
  13. break;
  14. case MESSAGE_POST_PROGRESS:
  15. result.mTask.onProgressUpdate(result.mData);
  16. break;
  17. }
  18. }
  19. }

通過代碼,我們看到如果what為MESSAGE_POST_PROGRESS,則會在主線程中調用onProgressUpdate(result.mData),這也就是為什麼我們平時在非同步線程調用publishProgress(Progress...)方法後,可以在主線程中的onProgressUpdate(rogress... values)接受數據了。

五、AsyncTask與Handler

AsyncTask:

  • 優點:AsyncTask是一個輕量級的非同步任務處理類,輕量級體現在,使用方便,代碼簡潔,而且整個非同步任務的過程可以通過cancel()進行控制
  • 缺點:不適用處理長時間的非同步任務,一般這個非同步任務的過程最好控制在幾秒以內,如果是長時間的非同步任務就需要考慮多線程的控制問題;當處理多個非同步任務時,UI更新變得困難。

Handler:

  • 優點:代碼結構清晰,容易處理多個非同步任務
  • 缺點:當有多個非同步任務時,由於要配合Thread或Runnable,代碼可能會稍顯冗餘。

總之,AsyncTask不失為一個非常好用的非同步任務處理類。不過我從事Android開發5年多了,很少會用到AsyncTask,一般非同步任務都是Handler。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 程序員小新人學習 的精彩文章:

SpringMVC工程的web.xml以及其他配置文件
較全Vim快捷鍵鍵點陣圖(入門到進階)

TAG:程序員小新人學習 |