Android AsyncTask的優(yōu)缺點(diǎn)詳解
1、Asynctask簡(jiǎn)介
1.1 使用方法簡(jiǎn)介
Asynctask作為Android的基礎(chǔ)之一,怎么使用就不多講解了,網(wǎng)上到處都是教程,建議查看Android官方API文檔:https://developer.android.google.cn/reference/android/os/AsyncTask.html
這里只實(shí)現(xiàn)一個(gè)小Demo程序,供大家賞玩:
界面:
這個(gè)程序其實(shí)特別簡(jiǎn)單,就是兩個(gè)按鈕,點(diǎn)擊分別用來(lái)測(cè)試AysncTask和Handler兩種模式的實(shí)現(xiàn),點(diǎn)擊后會(huì)有相應(yīng)的Log提示。
功能簡(jiǎn)介:
Asynctask的實(shí)現(xiàn):
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);//提交之后,會(huì)執(zhí)行onProcessUpdate方法 } Log.i(TAG, "doInBackground out"); return "over"; } /** * 在調(diào)用cancel方法后會(huì)執(zhí)行到這里 */ protected void onCancelled() { Log.i(TAG, "onCancelled"); } /** * 在doInbackground之后執(zhí)行 */ protected void onPostExecute(String args3) { Log.i(TAG, "onPostExecute:" + args3); } /** * 在doInBackground之前執(zhí)行 */ @Override protected void onPreExecute() { Log.i(TAG, "onPreExecute"); } /** * 特別贊一下這個(gè)多次參數(shù)的方法,特別方便 * @param args2 */ @Override protected void onProgressUpdate(Integer... args2) { Log.i(TAG, "onProgressUpdate:" + args2[0]); } }
點(diǎn)擊第一個(gè)按鈕后會(huì)執(zhí)行這里,點(diǎn)擊按鈕的寫(xiě)法如下:
mBtnSyncTask.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new IAsyncTask().execute("yanlog test"); } });
執(zhí)行結(jié)果的Log如下:
02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824進(jìn)程,11010線程中執(zhí)行 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//剩下的都是在10824線程中執(zhí)行,Android特別好的是,主線程的線程號(hào)跟進(jìn)程號(hào)是一致的 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:1 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over
Handler+Message實(shí)現(xiàn):
主要代碼如下:
private class IHandler extends Handler{ @Override public void handleMessage(Message msg){ switch(msg.what){ case 1: Log.e(TAG,"handler:"+msg.obj); break; default: break; } } }
其中,調(diào)用地方如下:
mBtnHandlerTest.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final Handler handler = new IHandler(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { Message msg = new Message(); msg.what = 1; msg.obj = new Integer(i); Log.e(TAG, "post message:" + i); handler.sendMessage(msg); } } }).start(); } });
可以看到Log打印結(jié)果如下:
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319號(hào)子進(jìn)程中提交 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:1 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //可以看到提交完是在9234主線程中執(zhí)行。 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:1 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9
以上,簡(jiǎn)單梳理了下怎么實(shí)現(xiàn),不贅言。
1.2 Android 內(nèi)部源碼實(shí)現(xiàn)
關(guān)于Handler+Message+Message Queue+Looper的實(shí)現(xiàn)就不介紹了,老生常談了。所以下面主要看一下AsyncTask的源碼實(shí)現(xiàn):
AsyncTask的核心方法應(yīng)該是
public final AsyncTask<Params, Progress, Result> execute(Params... params)
那我們就看下當(dāng)調(diào)用了execute方法后,都發(fā)生了什么,下面是執(zhí)行的序列圖。
我知道我畫(huà)的不夠標(biāo)準(zhǔn)了,湊合著看吧。下面關(guān)于這個(gè)圖的一些說(shuō)明。
- 在第4步,execute的時(shí)候,這個(gè)時(shí)候可以看到,doInBackground已經(jīng)轉(zhuǎn)到子線程中執(zhí)行了,這個(gè)是很關(guān)鍵的一個(gè)點(diǎn),我特意用了一個(gè)異步處理的箭頭標(biāo)注了。
- 在第9步,當(dāng)doInbackground執(zhí)行完,執(zhí)行到finish方法的時(shí)候,由通過(guò)sendMessage的方法回到了主線程中了,所以后面的onPostExecute和onCanceled都是在主線程中執(zhí)行的。
嗯,就這么多吧。關(guān)于AsyncTask的源碼我上傳到github中了,大家對(duì)照著源碼看會(huì)更清楚一點(diǎn)。
https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java
關(guān)于AsyncTask的源碼分析,還有一篇博客寫(xiě)的很好,請(qǐng)參看:
http://chabaoo.cn/article/81939.htm
備注:
源碼里面還有三個(gè)地方值得深究下,分別是:
- FutureTask值得看下,回頭寫(xiě)了博客我把鏈接貼在這里
- AsyncTask中的SerialExecutor類(lèi)寫(xiě)的太漂亮了,回頭單獨(dú)寫(xiě)一個(gè)博客欣賞下。
- 關(guān)于上面的ThreadPollExecutor我其實(shí)沒(méi)有研究。。。回頭寫(xiě)個(gè)博客研究下。
2、優(yōu)點(diǎn)
簡(jiǎn)單,快捷
這個(gè)說(shuō)法就是近乎于扯淡吧,主要還是看使用習(xí)慣,我就挺喜歡用Handler的。
但是Android定義了這個(gè)東西,可以看到各種消息封裝的還是很不錯(cuò)的,很規(guī)范。大家可以按照這個(gè)“優(yōu)美的框架”來(lái)寫(xiě),代碼不會(huì)太出格。
3、缺點(diǎn)
3.1 AsyncTask實(shí)際上后臺(tái)線程之后一個(gè)?。?!
今天仔細(xì)研究了下源碼,發(fā)現(xiàn)網(wǎng)上寫(xiě)的大部分是錯(cuò)的,AsyncTask的真正的后臺(tái)線程只有一個(gè)?。〔恍?,看下面的代碼:
我們首先定義一個(gè)IAsyncTAsk,其中的doInBackground方法這么寫(xiě):
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);//提交之后,會(huì)執(zhí)行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]); //這個(gè)doInBackground就打印一個(gè)Log,然后sleep 20 毫秒 Thread.sleep(20); } } catch (Exception e) { } return "over"; }
調(diào)用的地方這么寫(xiě):
int N = 5; for (int i = 0; i < N; i++) { Log.d(TAG,"asyncTask post Task:"+i); new IAsyncTask().execute("asyncTask times:"+i); //點(diǎn)擊Button后,在onClick方法中建立5個(gè)后臺(tái)子線程。 }
我們來(lái)看打印結(jié)果:
02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主線程中進(jìn)行提交操作 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:1 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //可以看到,雖然系統(tǒng)開(kāi)起了18067、18068、 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070這幾個(gè)子線程,但是這幾個(gè)子線程 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串行執(zhí)行的?。?!震驚了有沒(méi)有!??! 02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //這不是巧合,試了好幾次都是這樣??! 02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:0 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1 02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1 02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1 02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2 02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2 02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2 02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3 02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3 02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3 02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4 02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4 02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4 02-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4
你本來(lái)希望系統(tǒng)應(yīng)該這么執(zhí)行
但是實(shí)際上系統(tǒng)是這么執(zhí)行的:
那么從源碼看下為啥會(huì)這樣吧。
AsyncTask中默認(rèn)的Exector是這個(gè)private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是這么定義的
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //這是一個(gè)串行處理的Executor ......................... private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { //先把要執(zhí)行的子線程統(tǒng)一丟到mTasks隊(duì)列中,這其中封裝一遍Runnable public void run() { try { r.run(); } finally { scheduleNext(); //當(dāng)前面一個(gè)子線程處理完,開(kāi)始處理下一個(gè) } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { //依次從隊(duì)列中取下一個(gè)元素,串行執(zhí)行 if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
呵呵,這下子明白了吧。
Google為什么要怎么實(shí)現(xiàn)我不得而知,估計(jì)有什么我沒(méi)有明白的好處在里面吧。那么有沒(méi)有辦法規(guī)避呢?
可以看到上面有一個(gè)THREAD_POOL_EXECUTOR,這個(gè)也是一個(gè)executor是這么定義的
static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; }
可以看到這個(gè)是允許一定數(shù)量的子線程并行處理的。
其中參數(shù)是這么定義的
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;
按照一般理解,允許同時(shí)運(yùn)行的CORE進(jìn)程是4個(gè),MAXIMUM_POOL_SIZE是17個(gè)。(注:這個(gè)數(shù)字是我用榮耀8手機(jī)跑出來(lái)的,其他手機(jī)可能會(huì)有不同)
而Android中的AsyncTask提供了一個(gè)方法:
public static void setDefaultExecutor(Executor exec) { sDefaultExecutor = exec; }
所以規(guī)避方法是:通過(guò)這個(gè)方法可以設(shè)置默認(rèn)的Exector,但是這個(gè)方法是hide的,也就是Google的隱藏方法,估計(jì)需要用一下反射來(lái)處理。我偷個(gè)懶,不去實(shí)現(xiàn)了。
4、總結(jié)
本來(lái)嘛,我只是想簡(jiǎn)單寫(xiě)一下AsyncTask的一些相關(guān)知識(shí),Copy一下網(wǎng)上的內(nèi)容,但是沒(méi)有想到寫(xiě)到最后,發(fā)現(xiàn)網(wǎng)上的大部分東西是錯(cuò)的,或者沒(méi)有抓到重點(diǎn)。看來(lái)以后還是要自己親自看代碼,紙上得來(lái)終覺(jué)淺,便知此事要躬行。
本文中用到的工程代碼可以到我的github中查看,路徑:https://github.com/YanYoJun/AsyncTaskTest
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
- 淺談Android AsyncTask內(nèi)存安全的一種使用方式
- 詳解Spring/Spring boot異步任務(wù)編程WebAsyncTask
- Spring Boot 使用WebAsyncTask異步返回結(jié)果
- Android中使用AsyncTask實(shí)現(xiàn)下載文件動(dòng)態(tài)更新進(jìn)度條功能
- Android AsyncTask的缺陷和問(wèn)題總結(jié)
- Android使用AsyncTask下載圖片并顯示進(jìn)度條功能
- Android帶進(jìn)度條的下載圖片示例(AsyncTask異步任務(wù))
- Android 中使用 AsyncTask 異步讀取網(wǎng)絡(luò)圖片
- Android使用AsyncTask加載圖片的操作流程
相關(guān)文章
Kotlin文件讀寫(xiě)與SharedPreferences存儲(chǔ)功能實(shí)現(xiàn)方法
SharedPreferences是安卓平臺(tái)上一個(gè)輕量級(jí)的存儲(chǔ)類(lèi),用來(lái)保存應(yīng)用的一些常用配置,比如Activity狀態(tài),Activity暫停時(shí),將此activity的狀態(tài)保存到SharedPereferences中;當(dāng)Activity重載,系統(tǒng)回調(diào)方法onSaveInstanceState時(shí),再?gòu)腟haredPreferences中將值取出2022-12-12Android Handler的postDelayed()關(guān)閉的方法及遇到問(wèn)題
這篇文章主要介紹了Android Handler的postDelayed()關(guān)閉的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04Android中g(shù)oogle Zxing實(shí)現(xiàn)二維碼與條形碼掃描
這篇文章主要介紹了Android中g(shù)oogle Zxing實(shí)現(xiàn)二維碼與條形碼掃描的相關(guān)資料,需要的朋友可以參考下2017-05-05Android Studio使用教程(六):Gradle多渠道打包
這篇文章主要介紹了Android Studio使用教程(六):Gradle多渠道打包,本文講解了友盟多渠道打包、assemble結(jié)合Build Variants來(lái)創(chuàng)建task、完整的gradle腳本等內(nèi)容,需要的朋友可以參考下2015-05-05深入分析Android NFC技術(shù) android nfc開(kāi)發(fā)
本篇文章我們對(duì)android開(kāi)發(fā)中nfc技術(shù)做了全面的原理分析以及實(shí)現(xiàn)過(guò)程,需要的讀者們一起參考一下吧。2017-11-11android視頻播放簡(jiǎn)單實(shí)現(xiàn)示例(VideoView&MediaPlayer)
本篇文章主要介紹了android視頻播放簡(jiǎn)單實(shí)現(xiàn)示例(VideoView&MediaPlayer),具有一定的參考價(jià)值,有興趣的可以了解一下2017-08-08