導語:之前做習慣了Framework層的開發,今天在武漢鬥魚公司面試APP客戶端的開發,其中一道題是講述Asynctask的優缺點,我靠,我只是知道有這麼一個東西,會用而已,看來之前的生活太過於安逸,這麼基礎的東西都不會,特意回家總結下。 轉載請註明原文鏈接:http://www.cnblogs.c ...
導語:之前做習慣了Framework層的開發,今天在武漢鬥魚公司面試APP客戶端的開發,其中一道題是講述Asynctask的優缺點,我靠,我只是知道有這麼一個東西,會用而已,看來之前的生活太過於安逸,這麼基礎的東西都不會,特意回家總結下。
轉載請註明原文鏈接:http://www.cnblogs.com/yanyojun/p/6414919.html
1、Asynctask簡介
1.1 使用方法簡介
Asynctask作為Android的基礎之一,怎麼使用就不多講解了,網上到處都是教程,建議查看Android官方API文檔:https://developer.android.google.cn/reference/android/os/AsyncTask.html
這裡只實現一個小Demo程式,供大家賞玩:
界面:
這個程式其實特別簡單,就是兩個按鈕,點擊分別用來測試AysncTask和Handler兩種模式的實現,點擊後會有相應的Log提示。
功能簡介:
Asynctask的實現:
private class IAsyncTask extends AsyncTask<String, Integer, String> { protected String doInBackground(String... args1) { Log.i(TAG, "doInBackground in:" + args1[0]); int times = 10; for (int i = 0; i < times; i++) { publishProgress(i);//提交之後,會執行onProcessUpdate方法 } Log.i(TAG, "doInBackground out"); return "over"; } /** * 在調用cancel方法後會執行到這裡 */ protected void onCancelled() { Log.i(TAG, "onCancelled"); } /** * 在doInbackground之後執行 */ protected void onPostExecute(String args3) { Log.i(TAG, "onPostExecute:" + args3); } /** * 在doInBackground之前執行 */ @Override protected void onPreExecute() { Log.i(TAG, "onPreExecute"); } /** * 特別贊一下這個多次參數的方法,特別方便 * @param args2 */ @Override protected void onProgressUpdate(Integer... args2) { Log.i(TAG, "onProgressUpdate:" + args2[0]); } }
點擊第一個按鈕後會執行這裡,點擊按鈕的寫法如下:
mBtnSyncTask.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new IAsyncTask().execute("yanlog test"); } });
執行結果的Log如下:
1 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824進程,11010線程中執行 2 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out 3 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//剩下的都是在10824線程中執行,Android特別好的是,主線程的線程號跟進程號是一致的 4 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:1 5 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2 6 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3 7 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4 8 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5 9 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6 10 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7 11 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8 12 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9 13 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over
Handler+Message實現:
主要代碼如下:
1 private class IHandler extends Handler{ 2 @Override 3 public void handleMessage(Message msg){ 4 switch(msg.what){ 5 case 1: 6 Log.e(TAG,"handler:"+msg.obj); 7 break; 8 default: 9 break; 10 } 11 } 12 }
其中,調用地方如下:
1 mBtnHandlerTest.setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View v) { 4 final Handler handler = new IHandler(); 5 new Thread(new Runnable() { 6 @Override 7 public void run() { 8 for (int i = 0; i < 10; i++) { 9 Message msg = new Message(); 10 msg.what = 1; 11 msg.obj = new Integer(i); 12 Log.e(TAG, "post message:" + i); 13 handler.sendMessage(msg); 14 } 15 } 16 }).start(); 17 } 18 });
可以看到Log列印結果如下:
1 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319號子進程中提交 2 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:1 3 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2 4 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3 5 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4 6 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5 7 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6 8 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7 9 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8 10 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9 11 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //可以看到提交完是在9234主線程中執行。 12 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:1 13 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2 14 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3 15 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4 16 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5 17 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6 18 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7 19 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8 20 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9
以上,簡單梳理了下怎麼實現,不贅言。
1.2 Android 內部源碼實現
關於Handler+Message+Message Queue+Looper的實現就不介紹了,老生常談了。所以下麵主要看一下AsyncTask的源碼實現:
AsyncTask的核心方法應該是
public final AsyncTask<Params, Progress, Result> execute(Params... params)
那我們就看下當調用了execute方法後,都發生了什麼,下麵是執行的序列圖。
我知道我畫的不夠標準了,湊合著看吧。下麵關於這個圖的一些說明。
- 在第4步,execute的時候,這個時候可以看到,doInBackground已經轉到子線程中執行了,這個是很關鍵的一個點,我特意用了一個非同步處理的箭頭標註了。
- 在第9步,當doInbackground執行完,執行到finish方法的時候,由通過sendMessage的方法回到了主線程中了,所以後面的onPostExecute和onCanceled都是在主線程中執行的。
嗯,就這麼多吧。關於AsyncTask的源碼我上傳到github中了,大家對照著源碼看會更清楚一點。
https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java
關於AsyncTask的源碼分析,還有一篇博客寫的很好,請參看:
http://www.infocool.net/kb/OtherMobile/201610/197431.html
備註:
源碼裡面還有三個地方值得深究下,分別是:
- FutureTask值得看下,回頭寫了博客我把鏈接貼在這裡
- AsyncTask中的SerialExecutor類寫的太漂亮了,回頭單獨寫一個博客欣賞下。
- 關於上面的ThreadPollExecutor我其實沒有研究。。。回頭寫個博客研究下。
2、優點
簡單,快捷
這個說法就是近乎於扯淡吧,主要還是看使用習慣,我就挺喜歡用Handler的。
但是Android定義了這個東西,可以看到各種消息封裝的還是很不錯的,很規範。大家可以按照這個“優美的框架”來寫,代碼不會太出格。
3、缺點
3.1 AsyncTask實際上後臺線程之後一個!!!
今天仔細研究了下源碼,發現網上寫的大部分是錯的,AsyncTask的真正的後臺線程只有一個!!不信,看下麵的代碼:
我們首先定義一個IAsyncTAsk,其中的doInBackground方法這麼寫:
private class IAsyncTask extends AsyncTask<String, Integer, String> { protected String doInBackground(String... args1) { /* Log.i(TAG, "doInBackground in:" + args1[0]); int times = 10; for (int i = 0; i < times; i++) { publishProgress(i);//提交之後,會執行onProcessUpdate方法 } Log.i(TAG, "doInBackground out");*/ Log.i(TAG, "doInBackground in thread:" + args1[0]); try { int times = 4; for (int i = 0; i < times; i++) { Log.i(TAG, "thread alive:" + i + " for times"+args1[0]); //這個doInBackground就列印一個Log,然後sleep 20 毫秒 Thread.sleep(20); } } catch (Exception e) { } return "over"; }
調用的地方這麼寫:
1 int N = 5; 2 for (int i = 0; i < N; i++) { 3 Log.d(TAG,"asyncTask post Task:"+i); 4 new IAsyncTask().execute("asyncTask times:"+i); //點擊Button後,在onClick方法中建立5個後臺子線程。 5 }
我們來看列印結果:
1 02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主線程中進行提交操作 2 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:1 3 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2 4 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3 5 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4 6 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //可以看到,雖然系統開起了18067、18068、 7 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070這幾個子線程,但是這幾個子線程 8 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串列執行的!!!震驚了有沒有!!! 9 02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //這不是巧合,試了好幾次都是這樣!! 10 02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:0 11 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1 12 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1 13 02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1 14 02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1 15 02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1 16 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2 17 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2 18 02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2 19 02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2 20 02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2 21 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3 22 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3 23 02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3 24 02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3 25 02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3 26 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4 27 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4 28 02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4 29 02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4 30 02-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4
你本來希望系統應該這麼執行
但是實際上系統是這麼執行的:
那麼從源碼看下為啥會這樣吧。
AsyncTask中預設的Exector是這個private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是這麼定義的
1 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //這是一個串列處理的Executor 2 ......................... 3 private static class SerialExecutor implements Executor { 4 final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); 5 Runnable mActive; 6 7 public synchronized void execute(final Runnable r) { 8 mTasks.offer(new Runnable() { //先把要執行的子線程統一丟到mTasks隊列中,這其中封裝一遍Runnable 9 public void run() { 10 try { 11 r.run(); 12 } finally { 13 scheduleNext(); //當前面一個子線程處理完,開始處理下一個 14 } 15 } 16 }); 17 if (mActive == null) { 18 scheduleNext(); 19 } 20 } 21 22 protected synchronized void scheduleNext() { //依次從隊列中取下一個元素,串列執行 23 if ((mActive = mTasks.poll()) != null) { 24 THREAD_POOL_EXECUTOR.execute(mActive); 25 } 26 } 27 }
呵呵,這下子明白了吧。
Google為什麼要怎麼實現我不得而知,估計有什麼我沒有明白的好處在裡面吧。那麼有沒有辦法規避呢?
可以看到上面有一個THREAD_POOL_EXECUTOR,這個也是一個executor是這麼定義的
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 }
可以看到這個是允許一定數量的子線程並行處理的。
其中參數是這麼定義的
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30;
按照一般理解,允許同時運行的CORE進程是4個,MAXIMUM_POOL_SIZE是17個。(註:這個數字是我用榮耀8手機跑出來的,其他手機可能會有不同)
而Android中的AsyncTask提供了一個方法:
1 public static void setDefaultExecutor(Executor exec) { 2 sDefaultExecutor = exec; 3 }
所以規避方法是:通過這個方法可以設置預設的Exector,但是這個方法是hide的,也就是Google的隱藏方法,估計需要用一下反射來處理。我偷個懶,不去實現了。
4、總結
本來嘛,我只是想簡單寫一下AsyncTask的一些相關知識,Copy一下網上的內容,但是沒有想到寫到最後,發現網上的大部分東西是錯的,或者沒有抓到重點。看來以後還是要自己親自看代碼,紙上得來終覺淺,便知此事要躬行。
本文中用到的工程代碼可以到我的github中查看,路徑:https://github.com/YanYoJun/AsyncTaskTest
好了,寫完了,大家看到之後覺得還可以的話,點個贊哈。