Android AsyncTask原理-源碼層分析

1、概述

相信大家對AsyncTask都不陌生,對於執行耗時任務,然後更新UI是一把利器,當然也是替代Thread + Handler 的一種方式。如果你對Handler機制還不瞭解,請看:Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係

2、簡單的例子

相信大家都寫過這樣的代碼:

  1. package com.example.zhy_asynctask_demo01;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.ProgressDialog;  
  5. import android.os.AsyncTask;  
  6. import android.os.Bundle;  
  7. import android.util.Log;  
  8. import android.widget.TextView;  
  9.   
  10. public class MainActivity extends Activity  
  11. {  
  12.   
  13.     private static final String TAG = "MainActivity";  
  14.     private ProgressDialog mDialog;  
  15.     private TextView mTextView;  
  16.   
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState)  
  19.     {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.activity_main);  
  22.   
  23.         mTextView = (TextView) findViewById(R.id.id_tv);  
  24.   
  25.         mDialog = new ProgressDialog(this);  
  26.         mDialog.setMax(100);  
  27.         mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  28.         mDialog.setCancelable(false);  
  29.   
  30.         new MyAsyncTask().execute();  
  31.   
  32.     }  
  33.   
  34.     private class MyAsyncTask extends AsyncTask<Void, Integer, Void>  
  35.     {  
  36.   
  37.         @Override  
  38.         protected void onPreExecute()  
  39.         {  
  40.             mDialog.show();  
  41.             Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");  
  42.         }  
  43.   
  44.         @Override  
  45.         protected Void doInBackground(Void... params)  
  46.         {  
  47.   
  48.             // 模擬數據的加載,耗時的任務  
  49.             for (int i = 0; i < 100; i++)  
  50.             {  
  51.                 try  
  52.                 {  
  53.                     Thread.sleep(80);  
  54.                 } catch (InterruptedException e)  
  55.                 {  
  56.                     e.printStackTrace();  
  57.                 }  
  58.                 publishProgress(i);  
  59.             }  
  60.   
  61.             Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");  
  62.             return null;  
  63.         }  
  64.   
  65.         @Override  
  66.         protected void onProgressUpdate(Integer... values)  
  67.         {  
  68.             mDialog.setProgress(values[0]);  
  69.             Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");  
  70.         }  
  71.   
  72.         @Override  
  73.         protected void onPostExecute(Void result)  
  74.         {  
  75.             // 進行數據加載完成後的UI操作  
  76.             mDialog.dismiss();  
  77.             mTextView.setText("LOAD DATA SUCCESS ");  
  78.             Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");  
  79.         }  
  80.     }  
  81. }  

進入某個Activity,Activity中需要的數據來自於網絡或者其它耗時操作,可以在AsyncTask中onPreExecute完成一些準備操作,比如上例中顯示進度對話框;然後在doInBackground完成耗時操作,在進行耗時操作時還能不時的通過publishProgress給onProgressUpdate中傳遞參數,然後在onProgressUpdate中可以進行UI操作,比如上例更新進度條的進度;當耗時任務執行完成後,最後在onPostExecute進行設置控件數據更新UI等操作,例如隱藏進度對話框。

效果圖:


3、源碼解析

注:本篇源碼分析基於Andorid-17,因爲和3.0之前版本變動較大,有必要標出。

那麼大家一定好奇,AsyncTask在Android中是如何實現的,下面進行源碼分析:從我們的執行異步任務的起點開始,進入execute方法:

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  2.         return executeOnExecutor(sDefaultExecutor, params);  
  3. }  
  4. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  
  5.             Params... params) {  
  6.         if (mStatus != Status.PENDING) {  
  7.             switch (mStatus) {  
  8.                 case RUNNING:  
  9.                     throw new IllegalStateException("Cannot execute task:"  
  10.                             + " the task is already running.");  
  11.                 case FINISHED:  
  12.                     throw new IllegalStateException("Cannot execute task:"  
  13.                             + " the task has already been executed "  
  14.                             + "(a task can be executed only once)");  
  15.             }  
  16.         }  
  17.   
  18.         mStatus = Status.RUNNING;  
  19.   
  20.         onPreExecute();  
  21.   
  22.         mWorker.mParams = params;  
  23.         exec.execute(mFuture);  
  24.   
  25.         return this;  
  26.     }  
18行:設置當前AsyncTask的狀態爲RUNNING,上面的switch也可以看出,每個異步任務在完成前只能執行一次。
20行:執行了onPreExecute(),當前依然在UI線程,所以我們可以在其中做一些準備工作。
22行:將我們傳入的參數賦值給了mWorker.mParams
23行:exec.execute(mFuture)

相信大家對22行出現的mWorker,以及23行出現的mFuture都會有些困惑。
mWorker找到這個類:

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

可以看到是Callable的子類,且包含一個mParams用於保存我們傳入的參數,下面看初始化mWorker的代碼:
  1. public AsyncTask() {  
  2.         mWorker = new WorkerRunnable<Params, Result>() {  
  3.             public Result call() throws Exception {  
  4.                 mTaskInvoked.set(true);  
  5.   
  6.                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  7.                 //noinspection unchecked  
  8.                 return postResult(doInBackground(mParams));  
  9.             }  
  10.         };  
  11. //….  
  12.           
  13. }  

可以看到mWorker在構造方法中完成了初始化,並且因爲是一個抽象類,在這裏new了一個實現類,實現了call方法,call方法中設置mTaskInvoked=true,且最終調用doInBackground(mParams)方法,並返回Result值作爲參數給postResult方法.可以看到我們的doInBackground出現了,下面繼續看:
  1.   private Result postResult(Result result) {  
  2.         @SuppressWarnings("unchecked")  
  3.         Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  4.                 new AsyncTaskResult<Result>(this, result));  
  5.         message.sendToTarget();  
  6.         return result;  
  7. }  

可以看到postResult中出現了我們熟悉的異步消息機制,傳遞了一個消息message, message.what爲MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);
  1. private static class AsyncTaskResult<Data> {  
  2.        final AsyncTask mTask;  
  3.        final Data[] mData;  
  4.   
  5.        AsyncTaskResult(AsyncTask task, Data... data) {  
  6.            mTask = task;  
  7.            mData = data;  
  8.        }  
  9.    }  

AsyncTaskResult就是一個簡單的攜帶參數的對象。

看到這,我相信大家肯定會想到,在某處肯定存在一個sHandler,且複寫了其handleMessage方法等待消息的傳入,以及消息的處理。

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

哈哈,出現了我們的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息時,執行了result.mTask.finish(result.mData[0]);其實就是我們的AsyncTask.this.finish(result),於是看finish方法
  1. private void finish(Result result) {  
  2.         if (isCancelled()) {  
  3.             onCancelled(result);  
  4.         } else {  
  5.             onPostExecute(result);  
  6.         }  
  7.         mStatus = Status.FINISHED;  
  8.     }  

可以看到,如果我們調用了cancel()則執行onCancelled回調;正常執行的情況下調用我們的onPostExecute(result);主要這裏的調用是在handler的handleMessage中,所以是在UI線程中。如果你對異步消息機制不理解請看:Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係
最後將狀態置爲FINISHED。

mWoker看完了,應該到我們的mFuture了,依然實在構造方法中完成mFuture的初始化,將mWorker作爲參數,複寫了其done方法。

  1. public AsyncTask() {  
  2.     ...  
  3.         mFuture = new FutureTask<Result>(mWorker) {  
  4.             @Override  
  5.             protected void done() {  
  6.                 try {  
  7.                     postResultIfNotInvoked(get());  
  8.                 } catch (InterruptedException e) {  
  9.                     android.util.Log.w(LOG_TAG, e);  
  10.                 } catch (ExecutionException e) {  
  11.                     throw new RuntimeException("An error occured while executing doInBackground()",  
  12.                             e.getCause());  
  13.                 } catch (CancellationException e) {  
  14.                     postResultIfNotInvoked(null);  
  15.                 }  
  16.             }  
  17.         };  
  18. }  
16行:任務執行結束會調用:postResultIfNotInvoked(get());get()表示獲取mWorker的call的返回值,即Result.然後看postResultIfNotInvoked方法
  1. private void postResultIfNotInvoked(Result result) {  
  2.         final boolean wasTaskInvoked = mTaskInvoked.get();  
  3.         if (!wasTaskInvoked) {  
  4.             postResult(result);  
  5.         }  
  6. }  
如果mTaskInvoked不爲true,則執行postResult;但是在mWorker初始化時就已經將mTaskInvoked爲true,所以一般這個postResult執行不到。
好了,到了這裏,已經介紹完了execute方法中出現了mWorker和mFurture,不過這裏一直是初始化這兩個對象的代碼,並沒有真正的執行。下面我們看真正調用執行的地方。
execute方法中的:
還記得上面的execute中的23行:exec.execute(mFuture)

exec爲executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor

下面看這個sDefaultExecutor
  1. private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;  
  2. public static final Executor SERIAL_EXECUTOR = new SerialExecutor();  
  3. private static class SerialExecutor implements Executor {  
  4.         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  5.         Runnable mActive;  
  6.         public synchronized void execute(final Runnable r) {  
  7.             mTasks.offer(new Runnable() {  
  8.                 public void run() {  
  9.                     try {  
  10.                         r.run();  
  11.                     } finally {  
  12.                         scheduleNext();  
  13.                     }  
  14.                 }  
  15.             });  
  16.             if (mActive == null) {  
  17.                 scheduleNext();  
  18.             }  
  19.         }  
  20.         protected synchronized void scheduleNext() {  
  21.             if ((mActive = mTasks.poll()) != null) {  
  22.                 THREAD_POOL_EXECUTOR.execute(mActive);  
  23.             }  
  24.         }  
  25. }  
可以看到sDefaultExecutor其實爲SerialExecutor的一個實例,其內部維持一個任務隊列;直接看其execute(Runnable runnable)方法,將runnable放入mTasks隊尾;
16-17行:判斷當前mActive是否爲空,爲空則調用scheduleNext方法
20行:scheduleNext,則直接取出任務隊列中的隊首任務,如果不爲null則傳入THREAD_POOL_EXECUTOR進行執行。
下面看THREAD_POOL_EXECUTOR爲何方神聖:
  1. public static final Executor THREAD_POOL_EXECUTOR  
  2.           =new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  
  3.                     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  
可以看到就是一個自己設置參數的線程池,參數爲:
  1. private static final int CORE_POOL_SIZE = 5;  
  2. private static final int MAXIMUM_POOL_SIZE = 128;  
  3. private static final int KEEP_ALIVE = 1;  
  4. private static final ThreadFactory sThreadFactory = new ThreadFactory() {  
  5. private final AtomicInteger mCount = new AtomicInteger(1);  
  6. public Thread newThread(Runnable r) {  
  7.      return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());  
  8.     }  
  9.  };  
  10. private static final BlockingQueue<Runnable> sPoolWorkQueue =  
  11.             new LinkedBlockingQueue<Runnable>(10);  

看到這裏,大家可能會認爲,背後原來有一個線程池,且最大支持128的線程併發,加上長度爲10的阻塞隊列,可能會覺得就是在快速調用138個以內的AsyncTask子類的execute方法不會出現問題,而大於138則會拋出異常。
其實不是這樣的,我們再仔細看一下代碼,回顧一下sDefaultExecutor,真正在execute()中調用的爲sDefaultExecutor.execute:
  1. private static class SerialExecutor implements Executor {  
  2.         final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  3.         Runnable mActive;  
  4.         public synchronized void execute(final Runnable r) {  
  5.             mTasks.offer(new Runnable() {  
  6.                 public void run() {  
  7.                     try {  
  8.                         r.run();  
  9.                     } finally {  
  10.                         scheduleNext();  
  11.                     }  
  12.                 }  
  13.             });  
  14.             if (mActive == null) {  
  15.                 scheduleNext();  
  16.             }  
  17.         }  
  18.         protected synchronized void scheduleNext() {  
  19.             if ((mActive = mTasks.poll()) != null) {  
  20.                 THREAD_POOL_EXECUTOR.execute(mActive);  
  21.             }  
  22.         }  
  23. }  

可以看到,如果此時有10個任務同時調用execute(s synchronized)方法,第一個任務入隊,然後在mActive = mTasks.poll()) != null被取出,並且賦值給mActivte,然後交給線程池去執行。然後第二個任務入隊,但是此時mActive並不爲null,並不會執行scheduleNext();所以如果第一個任務比較慢,10個任務都會進入隊列等待;真正執行下一個任務的時機是,線程池執行完成第一個任務以後,調用Runnable中的finally代碼塊中的scheduleNext,所以雖然內部有一個線程池,其實調用的過程還是線性的。一個接着一個的執行,相當於單線程。

4、總結

到此源碼解釋完畢,由於代碼跨度比較大,我們再回顧一下:

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  2.         return executeOnExecutor(sDefaultExecutor, params);  
  3. }  
  4. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  
  5.             Params... params) {  
  6.         if (mStatus != Status.PENDING) {  
  7.             switch (mStatus) {  
  8.                 case RUNNING:  
  9.                     throw new IllegalStateException("Cannot execute task:"  
  10.                             + " the task is already running.");  
  11.                 case FINISHED:  
  12.                     throw new IllegalStateException("Cannot execute task:"  
  13.                             + " the task has already been executed "  
  14.                             + "(a task can be executed only once)");  
  15.             }  
  16.         }  
  17.   
  18.         mStatus = Status.RUNNING;  
  19.   
  20.         onPreExecute();  
  21.   
  22.         mWorker.mParams = params;  
  23.         exec.execute(mFuture);  
  24.   
  25.         return this;  
  26.     }  

18行:設置當前AsyncTask的狀態爲RUNNING,上面的switch也可以看出,每個異步任務在完成前只能執行一次。
20行:執行了onPreExecute(),當前依然在UI線程,所以我們可以在其中做一些準備工作。
22行:將我們傳入的參數賦值給了mWorker.mParams ,mWorker爲一個Callable的子類,且在內部的call()方法中,調用了doInBackground(mParams),然後得到的返回值作爲postResult的參數進行執行;postResult中通過sHandler發送消息,最終sHandler的handleMessage中完成onPostExecute的調用。
23行:exec.execute(mFuture),mFuture爲真正的執行任務的單元,將mWorker進行封裝,然後由sDefaultExecutor交給線程池進行執行。


5、publishProgress

說了這麼多,我們好像忘了一個方法:publishProgress

  1. protected final void publishProgress(Progress... values) {  
  2.         if (!isCancelled()) {  
  3.             sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  
  4.                     new AsyncTaskResult<Progress>(this, values)).sendToTarget();  
  5.         }  
  6. }  
也很簡單,直接使用sHandler發送一個消息,並且攜帶我們傳入的值;
  1. private static class InternalHandler extends Handler {  
  2.         @SuppressWarnings({"unchecked""RawUseOfParameterizedType"})  
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
  6.             switch (msg.what) {  
  7.                 case MESSAGE_POST_RESULT:  
  8.                     // There is only one result  
  9.                     result.mTask.finish(result.mData[0]);  
  10.                     break;  
  11.                 case MESSAGE_POST_PROGRESS:  
  12.                     result.mTask.onProgressUpdate(result.mData);  
  13.                     break;  
  14.             }  
  15.         }  
  16. }  

在handleMessage中進行了我們的onProgressUpdate(result.mData);的調用。

6、AsyncTask曾經缺陷

記得以前有個面試題經常會問道:AsyncTask運行的原理是什麼?有什麼缺陷?

以前對於缺陷的答案可能是:AsyncTask在併發執行多個任務時發生異常。其實還是存在的,在3.0以前的系統中還是會以支持多線程併發的方式執行,支持併發數也是我們上面所計算的128,阻塞隊列可以存放10個;也就是同時執行138個任務是沒有問題的;而超過138會馬上出現Java.util.concurrent.RejectedExecutionException;

而在在3.0以上包括3.0的系統中會爲單線程執行(即我們上面代碼的分析);

空說無憑:下面看測試代碼:

  1. package com.example.zhy_asynctask_demo01;  
  2.   
  3. import android.app.Activity;  
  4. import android.app.ProgressDialog;  
  5. import android.os.AsyncTask;  
  6. import android.os.Bundle;  
  7. import android.util.Log;  
  8. import android.widget.TextView;  
  9.   
  10. public class MainActivity extends Activity  
  11. {  
  12.   
  13.     private static final String TAG = "MainActivity";  
  14.     private ProgressDialog mDialog;  
  15.     private TextView mTextView;  
  16.   
  17.     @Override  
  18.     protected void onCreate(Bundle savedInstanceState)  
  19.     {  
  20.         super.onCreate(savedInstanceState);  
  21.         setContentView(R.layout.activity_main);  
  22.   
  23.         mTextView = (TextView) findViewById(R.id.id_tv);  
  24.   
  25.         mDialog = new ProgressDialog(this);  
  26.         mDialog.setMax(100);  
  27.         mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);  
  28.         mDialog.setCancelable(false);  
  29.           
  30.           
  31.         for(int i = 1 ;i <= 138 ; i++)  
  32.         {  
  33.             new MyAsyncTask2().execute();  
  34.         }  
  35.           
  36.         //new MyAsyncTask().execute();  
  37.   
  38.           
  39.     }  
  40.   
  41.     private class MyAsyncTask2 extends AsyncTask<Void,Void, Void>  
  42.     {  
  43.   
  44.         @Override  
  45.         protected Void doInBackground(Void... params)  
  46.         {  
  47.             try  
  48.             {  
  49.                 Log.e(TAG, Thread.currentThread().getName());  
  50.                 Thread.sleep(10000);  
  51.             } catch (InterruptedException e)  
  52.             {  
  53.                 e.printStackTrace();  
  54.             }  
  55.             return null;  
  56.         }  
  57.           
  58.     }  
  59. }  
可以看到我for循環中執行138個異步任務,每個異步任務的執行需要10s;下面使用2.2的模擬器進行測試:

輸出結果爲:

AsyncTask#1 - AsyncTask #128同時輸出
然後10s後,另外10個任務輸出。
可以分析結果,得到結論:AsyncTask在2.2的系統中同時支持128個任務併發,至少支持10個任務等待;

下面將138個任務,改成139個任務:

  1. for(int i = 1 ;i <= 139 ; i++)  
  2. {  
  3.     new MyAsyncTask2().execute();  
  4. }  
運行結果:會發生異常:java.util.concurrent.RejectedExecutionException ; 於是可以確定僅支持10個任務等待,超過10個則立即發生異常。
簡單說一下出現異常的原因:現在是139個任務,幾乎同時提交,線程池支持128個的併發,然後阻塞隊列數量爲10,此時當第11個任務提交的時候則會發生異常。

簡單看一下源碼:

  1. public static final Executor THREAD_POOL_EXECUTOR  
  2.            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);  
看ThreadPoolExecutor的execute方法:
  1. if (isRunning(c) && workQueue.offer(command)) {  
  2.             int recheck = ctl.get();  
  3.             if (! isRunning(recheck) && remove(command))  
  4.                 reject(command);  
  5.             else if (workerCountOf(recheck) == 0)  
  6.                 addWorker(nullfalse);  
  7.         }  
  8.         else if (!addWorker(command, false))  
  9.             reject(command);  

當阻塞隊列滿的時候workQueue.offer(command)返回false;然後執行addWorker(command,false)方法,如果返回false則執行reject()方法.
  1. private boolean addWorker(Runnable firstTask, boolean core) {  
  2. …  
  3. int wc = workerCountOf(c);  
  4.                 if (wc >= CAPACITY ||  
  5.                     wc >= (core ? corePoolSize : maximumPoolSize))  
  6.                     return false;  
  7. …  
  8. }  

可以看到當任務數目大於容量則返回false,最終在reject()中拋出異常。

上面就是使用2.2模擬器測試的結果;

下面將系統改爲4.1.1,也就是我的測試機小米2s

把線程數改爲139甚至1000,你可以看到任務一個接一個的在那緩慢的執行,不會拋什麼異常,不過線程倒是1個1個的在那執行;


好了,如果現在大家去面試,被問到AsyncTask的缺陷,可以分爲兩個部分說,在3.0以前,最大支持128個線程的併發,10個任務的等待。在3.0以後,無論有多少任務,都會在其內部單線程執行;


至此,AsyncTask源碼分析完畢,相信大家對AsyncTask有了更深的理解~~~


本帖轉自:http://blog.csdn.net/lmj623565791/article/details/38614699,本文出自:【張鴻洋的博客】

發佈了5 篇原創文章 · 獲贊 4 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章