
一、AsyncTask介紹
1.1 作用:
輕量級的異步類,同時方便在子線程更新UI。
1.2 原理:
封裝了Handler和兩個線程池。AsyncTask的源碼鏈接
-
線程池THREAD_POOL_EXECUTOR:通過ThreadFactory和一些參數實例化線程池
THREAD_POOL_EXECUTOR用于執(zhí)行任務。 -
線程池SERIAL_EXECUTOR:內部類SerialExecutor實現Executor也是一個線程池,實例化對象
SERIAL_EXECUTOR執(zhí)行SerialExecutor定義的execute方法來串行(一個一個按順序)排隊任務并使用THREAD_POOL_EXECUTOR來執(zhí)行任務。 - AsyncTask創(chuàng)建時會實例化一個WorkerRunnable對象mWorker和一個FutureTask對象mFuture。FutureTask是一個并發(fā)類,充當Runnable的作用。AsyncTask執(zhí)行任務時把mFuture作為排隊線程池SerialExecutor的
execute(final Runnable r)的參數傳遞,接著會串行排隊并使用工作線程來處理實際任務。 -
mFuture對象創(chuàng)建時把mWorker對象作為參數傳遞,在mFuture在執(zhí)行任務
run()時會調用mWorker的call()方法,因此call()方法是在線程池中進行的。 -
主線程的Handler:然后
call()方法里面接著調用result = doInBackground(mParams);并返回Result對象,這個過程中任務被取消會catch跳出設置AsyncTask結束。call()方法最后postResult(result);獲取主線程的Handler發(fā)送消息并根據消息類型判定更新UI或者結束AsyncTask。
具體參考:源碼解析Android中AsyncTask的工作原理
1.3 參數以及核心函數
AsyncTask是一個泛型類,在android.os包下

泛型內三個參數定義的是我們需要的數據的類型,如果不需要傳遞參數,可以用Void來代替,以下是具體含義:
- Params:參數的類型;
- Progress:后臺任務的執(zhí)行進度類型;
-
Result:后臺任務返回結果的類型。
AsyncTask還提供了4個核心方法:
-
protected void onPreExecute():在主線程執(zhí)行,異步任務執(zhí)行前調用做準備工作; -
protected abstract Result doInBackground(Params... var1):在線程池中執(zhí)行,用于執(zhí)行異步任務。Params表示異步任務輸入參數,可以在此方法中通過publishProgress方法來更新任務進度,publishProgress方法又調用onProgressUpdate方法實現主線程進度更新。這個方法返回Result給onPostExecute方法。 -
protected void onProgressUpdate(Progress... values):在主線程執(zhí)行,后臺任務執(zhí)行進度發(fā)生變化會調用此方法。 -
protected void onPostExecute(Result result):在主線程執(zhí)行,異步任務執(zhí)行后調用,result參數是由doInBackground返回的。
這四個方法執(zhí)行的順序為:onPreExecute(準備)-->doInBackground(后臺異步)-->onPostExecute(結果處理)。
還有一個有用的方法:protected void onCancelled():當異步任務取消時調用,而這時候onPostExecute不會被調用。
二、AsyncTask使用
2.1 AsyncTask使用示例:
(1) 創(chuàng)建Activity,布局兩個按鈕用來開始和取消AsyncTask,一個TextView來顯示狀態(tài)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/btn_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開始下載"/>
<Button
android:id="@+id/btn_cancel"
android:layout_below="@id/btn_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下載"/>
<TextView
android:id="@+id/tv_statue"
android:layout_below="@id/btn_cancel"
android:textSize="15sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
(2) 創(chuàng)建內部類自定義MyAsyncTask繼承AsyncTask,重寫四個主要方法:
//第一個參數:Params參數類型:String類型
//第二個參數:Progress執(zhí)行進度類型:Object類型
//第三個參數:Result參數類型:Long類型
private class MyAsyncTask extends AsyncTask<String,Object,Long>{
...
@Override
protected void onPreExecute() {...}
@Override
protected Long doInBackground(String... strings) {...}
@Override
protected void onProgressUpdate(Object... values) {...}
@Override
protected void onPostExecute(Long aLong) {...}
@Override //任務取消調用
protected void onCancelled() {...}
(3)開始按鈕調用創(chuàng)建MyAsyncTask并執(zhí)行downloadTask.execute(urls)傳入參數。
mDownButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String[] urls = {
"http://www.itdecent.cn/p/d3984e7e204f",
"http://www.itdecent.cn/p/8e3cfb87f35e",
"http://www.itdecent.cn/p/4fbc2ea2e02b"
};
downloadTask = new MyAsyncTask();
// 這里的url也可以分成一個一個參數傳遞
downloadTask.execute(urls);
}
});
(4) 取消按鈕調用downloadTask.cancel(true)停止MyAsyncTask。
//取消按鈕點擊事件
downloadTask.cancel(true);
(5) doInBackground(String... strings)方法循環(huán)執(zhí)行下載任務并返回result,是自己定義的二維數組,第一個是字節(jié)數,第二個是文章標題。
protected Long doInBackground(String... strings) {
long totalByte = 0;
for (String url:strings){
// downloadFile方法只是一個簡單的下載字節(jié)流
// 返回的Object[] result是二維數組,一個是下載的字節(jié)數,一個是標題
Object[] result = downloadFile(url);
int byteCount = (int) result[0];
totalByte += byteCount;
//傳遞進度
publishProgress(result);
// 異步任務調用取消則跳出for循環(huán)
if(isCancelled()){
break;
}
}
return totalByte;
}
(6) 然后使用publishProgress(result);把數據傳遞給onProgressUpdate(Object... values)方法來更新UI,這個方法已經切換到了主線程,所以可以更新UI。AsyncTask完成后onPostExecute(Long aLong)方法自動調用再次來更新狀態(tài)。
@Override
protected void onPostExecute(Long aLong) {...}
看一下效果,源碼我會在后面放出,取消下載請自行測試。

2.2 AsyncTask使用注意:
- AsyncTask的類必須在主線程加載。
- AsyncTask的對象必須在主線程創(chuàng)建。
- execute方法必須在UI線程調用。
- 一個AsyncTask對象只能執(zhí)行一次,即只能調用一次execute方法,否則報運行時異常。
- 在Android1.6之前,AsyncTask是串行執(zhí)行任務的,Android1.6的時候AsyncTask開始使用線程池處理并行任務,但是從Android3.0開始,為了避免AsyncTask所帶來的并發(fā)錯誤,AsyncTask又采用一個線程串行執(zhí)行任務。盡管如此,在Android3.0以及后續(xù)的版本中,我們仍然可以通過AsyncTask的executeOnExecutor方法來并行地執(zhí)行任務。
三、AsyncTask優(yōu)點和缺點
3.1 優(yōu)點:
- 簡單快捷,使用方便。
- UI更新及時,過程可控。
3.2 缺點:
多個異步操作需要更新UI時,就變得麻煩起來。
3.3 其它問題:
參考文章:AsyncTask的缺陷和問題
目錄文章:
Android多線程:理解和簡單使用總結