Android AsyncTask的優(yōu)缺點詳解
1、Asynctask簡介
1.1 使用方法簡介
Asynctask作為Android的基礎之一,怎么使用就不多講解了,網(wǎng)上到處都是教程,建議查看Android官方API文檔:https://developer.android.google.cn/reference/android/os/AsyncTask.html
這里只實現(xiàn)一個小Demo程序,供大家賞玩:
界面:

這個程序其實特別簡單,就是兩個按鈕,點擊分別用來測試AysncTask和Handler兩種模式的實現(xiàn),點擊后會有相應的Log提示。
功能簡介:
Asynctask的實現(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);//提交之后,會執(zhí)行onProcessUpdate方法
}
Log.i(TAG, "doInBackground out");
return "over";
}
/**
* 在調(diào)用cancel方法后會執(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");
}
/**
* 特別贊一下這個多次參數(shù)的方法,特別方便
* @param args2
*/
@Override
protected void onProgressUpdate(Integer... args2) {
Log.i(TAG, "onProgressUpdate:" + args2[0]);
}
}
點擊第一個按鈕后會執(zhí)行這里,點擊按鈕的寫法如下:
mBtnSyncTask.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new IAsyncTask().execute("yanlog test");
}
});
執(zhí)行結果的Log如下:
02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824進程,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特別好的是,主線程的線程號跟進程號是一致的 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實現(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打印結果如下:
02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319號子進程中提交 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
以上,簡單梳理了下怎么實現(xiàn),不贅言。
1.2 Android 內(nèi)部源碼實現(xiàn)
關于Handler+Message+Message Queue+Looper的實現(xiàn)就不介紹了,老生常談了。所以下面主要看一下AsyncTask的源碼實現(xiàn):
AsyncTask的核心方法應該是
public final AsyncTask<Params, Progress, Result> execute(Params... params)
那我們就看下當調(diào)用了execute方法后,都發(fā)生了什么,下面是執(zhí)行的序列圖。

我知道我畫的不夠標準了,湊合著看吧。下面關于這個圖的一些說明。
- 在第4步,execute的時候,這個時候可以看到,doInBackground已經(jīng)轉到子線程中執(zhí)行了,這個是很關鍵的一個點,我特意用了一個異步處理的箭頭標注了。
- 在第9步,當doInbackground執(zhí)行完,執(zhí)行到finish方法的時候,由通過sendMessage的方法回到了主線程中了,所以后面的onPostExecute和onCanceled都是在主線程中執(zhí)行的。
嗯,就這么多吧。關于AsyncTask的源碼我上傳到github中了,大家對照著源碼看會更清楚一點。
https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java
關于AsyncTask的源碼分析,還有一篇博客寫的很好,請參看:
http://m.fzitv.net/article/81939.htm
備注:
源碼里面還有三個地方值得深究下,分別是:
- FutureTask值得看下,回頭寫了博客我把鏈接貼在這里
- AsyncTask中的SerialExecutor類寫的太漂亮了,回頭單獨寫一個博客欣賞下。
- 關于上面的ThreadPollExecutor我其實沒有研究。。?;仡^寫個博客研究下。
2、優(yōu)點
簡單,快捷
這個說法就是近乎于扯淡吧,主要還是看使用習慣,我就挺喜歡用Handler的。
但是Android定義了這個東西,可以看到各種消息封裝的還是很不錯的,很規(guī)范。大家可以按照這個“優(yōu)美的框架”來寫,代碼不會太出格。
3、缺點
3.1 AsyncTask實際上后臺線程之后一個?。。?/strong>
今天仔細研究了下源碼,發(fā)現(xiàn)網(wǎng)上寫的大部分是錯的,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);//提交之后,會執(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]); //這個doInBackground就打印一個Log,然后sleep 20 毫秒
Thread.sleep(20);
}
} catch (Exception e) {
}
return "over";
}
調(diào)用的地方這么寫:
int N = 5;
for (int i = 0; i < N; i++) {
Log.d(TAG,"asyncTask post Task:"+i);
new IAsyncTask().execute("asyncTask times:"+i); //點擊Button后,在onClick方法中建立5個后臺子線程。
}
我們來看打印結果:
02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主線程中進行提交操作 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)開起了18067、18068、 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070這幾個子線程,但是這幾個子線程 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串行執(zhí)行的?。?!震驚了有沒有?。?! 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
你本來希望系統(tǒng)應該這么執(zhí)行

但是實際上系統(tǒng)是這么執(zhí)行的:

那么從源碼看下為啥會這樣吧。
AsyncTask中默認的Exector是這個private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是這么定義的
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //這是一個串行處理的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隊列中,這其中封裝一遍Runnable
public void run() {
try {
r.run();
} finally {
scheduleNext(); //當前面一個子線程處理完,開始處理下一個
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() { //依次從隊列中取下一個元素,串行執(zhí)行
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
呵呵,這下子明白了吧。
Google為什么要怎么實現(xiàn)我不得而知,估計有什么我沒有明白的好處在里面吧。那么有沒有辦法規(guī)避呢?
可以看到上面有一個THREAD_POOL_EXECUTOR,這個也是一個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;
}
可以看到這個是允許一定數(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;
按照一般理解,允許同時運行的CORE進程是4個,MAXIMUM_POOL_SIZE是17個。(注:這個數(shù)字是我用榮耀8手機跑出來的,其他手機可能會有不同)
而Android中的AsyncTask提供了一個方法:
public static void setDefaultExecutor(Executor exec) {
sDefaultExecutor = exec;
}
所以規(guī)避方法是:通過這個方法可以設置默認的Exector,但是這個方法是hide的,也就是Google的隱藏方法,估計需要用一下反射來處理。我偷個懶,不去實現(xiàn)了。
4、總結
本來嘛,我只是想簡單寫一下AsyncTask的一些相關知識,Copy一下網(wǎng)上的內(nèi)容,但是沒有想到寫到最后,發(fā)現(xiàn)網(wǎng)上的大部分東西是錯的,或者沒有抓到重點??磥硪院筮€是要自己親自看代碼,紙上得來終覺淺,便知此事要躬行。
本文中用到的工程代碼可以到我的github中查看,路徑:https://github.com/YanYoJun/AsyncTaskTest
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
- 淺談Android AsyncTask內(nèi)存安全的一種使用方式
- 詳解Spring/Spring boot異步任務編程WebAsyncTask
- Spring Boot 使用WebAsyncTask異步返回結果
- Android中使用AsyncTask實現(xiàn)下載文件動態(tài)更新進度條功能
- Android AsyncTask的缺陷和問題總結
- Android使用AsyncTask下載圖片并顯示進度條功能
- Android帶進度條的下載圖片示例(AsyncTask異步任務)
- Android 中使用 AsyncTask 異步讀取網(wǎng)絡圖片
- Android使用AsyncTask加載圖片的操作流程
相關文章
flutter PositionedTransition實現(xiàn)縮放動畫
這篇文章主要為大家詳細介紹了flutter PositionedTransition實現(xiàn)縮放動畫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07
Androidstudio調(diào)用攝像頭拍照并保存照片
這篇文章主要為大家詳細介紹了Androidstudio調(diào)用攝像頭拍照并保存照片,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03
Android開發(fā)手冊Chip監(jiān)聽及ChipGroup監(jiān)聽
這篇文章主要為大家介紹了Android開發(fā)手冊Chip監(jiān)聽及ChipGroup監(jiān)聽,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06
Android中Java根據(jù)文件頭獲取文件類型的方法
這篇文章主要介紹了Android中Java根據(jù)文件頭獲取文件類型的方法,涉及Android針對文件屬性的相關操作技巧,需要的朋友可以參考下2016-08-08

