AsyncTask
AsyncTask 是一個輕量級的異步任務類,它可以在線程池中執行後臺任務,然後把執行的進入和最終結果傳遞給主線程並在主線程中更新UI。它封裝了Thread 和Handler,但是AsyncTask 不適合執行特別耗時的後臺任務,對於特別耗時的任務建議採取使用線程池。
AsyncTask是一個抽象類,所以如果我們想使用它,就必須要創建一個子類去繼承它。在繼承時我們可以爲AsyncTask類指定三個泛型參數,這三個參數的用途如下,如果不需要某個參數,可以設置爲 Void
1. Params(參數類型)
在執行AsyncTask時需要傳入的參數,可用於在後臺任務中使用,是excute()任務執行方法和 doInBackground(Params...)的輸入參數,通常爲String
2. Progress(後臺任務執行的進度類型)
後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位,是 onProgressUpdate(Progress...)和 publishProgress(Progress...)方法的參數,是一個數組類型的參數,一般爲Integer
3. Result(返回的結果類型)
當任務執行完畢後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型,是onPostExecute(Result)方法的輸入參數
如果AsyncTask 確實不需要傳遞具體的參數,這三個泛型參數可以使用Void 替代。
AsyncTask 的聲明如下所示:
public abstract class AsyncTask<Params, Progress, Result> {
因此,一個最簡單的自定義AsyncTask就可以寫成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}
這裏我們把AsyncTask的第一個泛型參數指定爲Void,表示在執行AsyncTask的時候不需要傳入參數給後臺任務。第二個泛型參數指定爲Integer,表示使用整型數據來作爲進度顯示單位。第三個泛型參數指定爲Boolean,則表示使用布爾型數據來反饋執行結果。
一個異步任務的執行一般包括以下幾個步驟。
1.execute(Parmas...parmas)
執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行
2. onPreExecute()
執行在 UI 線程,在execute(Parmas...parmas)被調用後立即執行,這個方法會在後臺任務開始執行之前調用,調用後立即執行,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
3. doInBackground(Params...)
在線程池中執行,在onPreExecute() 完成後立即執行,這個方法中的所有代碼都會在子線程中運行,我們應該在這裏去處理所有的耗時任務。此方法將接收輸入參數和返回計算結果,並將計算結果給onPostExecute 方法。在執行過程中可以調用 publishProgress(Progress...) 方法來更新任務進度信息。publishProgress 會調用 onProgressUpdate 方法。
4. onProgressUpdate(Progress...)
執行在 UI 線程,當在後臺任務中調用了 publishProgress(Progress...) 方法後,這個方法就會很快被調用,直接將進度信息更新到 UI 組件上。
5. onPostExecute(Result)
執行在 UI 線程,當後臺操作結束時,該方法被調用,doInBackground(Params...) 函數返回的計算結果將作爲參數傳遞到此方法中,直接將結果顯示到 UI 組件上。比如說提醒任務執行的結果,以及關閉掉進度條對話框等。
上面這幾個方法,onPreExecute 先執行,接着是doInBackground,最後纔是onPostExecute。除了上述四個方法以外,AsyncTask 還提供了 onCancelled 方法,它同樣執行在UI 線程,當異步任務被取消時,onCancelled 方法會被調用,此時onPostExecute 方法則不會被調用。如下所示:
public class DownloadFilesTask
extends AsyncTask<URL, Integer, Long>
{
//執行在線程池中
@Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) (i / (float) count) * 100);//更新下載任務進度
if (isCancelled()) {//判斷下載任務是否取消
break;
}
}
return totalSize;
}
@Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
@Override
protected void onPostExecute(Long result) {
showDialog("Downloaded" + result + "bttes");
}
}
注意doInBackground 和 onProgressUpdate 方法他們的參數中均包含 ... 的字樣,在Java 中 ... 表示參數的數量不定,它是一種數組類型的參數。當要執行上述下載任務時,可以通過如下方式來完成:
new DownloadFilesTask().execute(url1,url2.url3);
在使用的時候,有以下幾點需要格外注意(AsyncTask 的侷限):
1、AsyncTask 對象必須在 UI 線程中創建,因爲在AsyncTask 中會構建一個 Handler
2、execute(Parmas...parmas)方法必須在 UI 線程中調用,因爲在AsyncTask 中會構建一個 Handler
3、不能在 doInBackground(Params...) 中更改 UI 組件信息,因爲在子線程中執行
4、不要在程序中直接調用 onPreExecute()、onPostExecute()、doInBackground() 和 onProgressUpdate() 方法
5、一個任務實例只能執行一次,如果執行第二次將會拋出異常
6、在Android1.6 之前,AsyncTask 是串行執行任務的,在Android 1.6 AsyncTask 採用線程池裏處理並行任務,但從Android 3.0 開始,爲避免AsyncTask 所帶來的併發錯誤,AsyncTask 又採用一個線程來串行執行任務。在Android 3.0 之後的版本中,我們仍然可以通過AsyncTask 的 executeOnExecutor 方法來並行的執行任務。
AsyncTask 的工作原理:
分析AsyncTask 的工作原理,從它的excute 方法開始,如下所示:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute 方法會調用 executeOnExecutor 方法,源碼如下所示:
private final WorkerRunnable<Params, Result> mWorker;
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {//判斷當前任務的狀態
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;//新任務則將狀態置爲執行狀態
onPreExecute();
mWorker.mParams = params;//將參數賦值給mWorker
exec.execute(mFuture);//mFuture 會交給 SerialExecutor 的 execute 方法處理
return this;
}
在上述代碼中,sDefaultExecutor 實際上是一個串行的線程池,一個進程中所有的AsyncTask 全部在這個串行的線程池中排隊執行,在 executeOnExecutor 方法中,AsyncTask 的onPreExecute 方法最先執行,然後線程池開始執行,其執行過程如下所示:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//將FutureTask 對象插入到任務隊列 mTasks 中,
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
//如果當前沒有正在活動的AsyncTask,則調用scheduleNext
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {//執行下一個AsyncTask 任務
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
從上面代碼可知,首先系統會把AsyncTask 的Params 參數封裝爲 FutureTask 對象,FutureTask 是一個併發類,在這裏充當了Runnable 的作用。接着這個 FutureTask 會交給 SerialExecutor 的 execute 方法處理,SerialExecutor 的 execute 方法首先會把FutureTask 對象插入到任務隊列 mTasks 中,如果這個時候沒有正在活動的AsyncTask,則調用scheduleNext方法來執行下一個AsyncTask 任務。 同時當一個AsyncTask 任務執行完後,AsyncTask 會繼續執行其他任務知道所有的任務都被執行爲止,從這點可以看出,默認情況下,AsyncTask 是串行執行的。
AsyncTask 中有兩個線程池(SerialExecutor 和 THREAD_POOL_EXECUTOR )和一個 Handler(InternalHandler),其中線程池SerialExecutor 用於任務的排隊,線程池 THREAD_POOL_EXECUTOR 用於正真的執行任務,InternalHandler 用於將執行環境榮線程池切換到主線程。THREAD_POOL_EXECUTOR 的定義如下所示:
public static final Executor THREAD_POOL_EXECUTOR;
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU核心數
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;//超時時間
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);//任務隊列容量
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;
}
AsyncTask 對 THREAD_POOL_EXECUTOR 的配置規格如上所示。
AsyncTask 的構造方法如下所示:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);//標記當前任務已經被調用過了
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);//調用doInBackground 並獲取返回值
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);//調用postResult 方法,並將返回值作爲參數傳遞過去
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
由於FutureTask 的run 方法會調用 mWorke 的call 方法,因此 mWorke 的call 方法最終會在線程池中執行。FutureTask 的run 方法如下所示:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在mWorke 的call 方法中,首先將 mTaskInvoked 設未 true,表明當前任務已經被調用過了,然後執行AsyncTask的 doInBackground 方法,並將返回值傳遞給 postResult 方法,postResult 方法如下所示:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
postResult 方法會通過 mHandler 發送一個 MESSAGE_POST_RESULT的消息,mHandler 的定義如下所示:
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
mHandler 是一個靜態的Handler 對象,爲了能夠將執行環境切換到主線程,這就要求 mHandler 這個對象必須在主線程中創建。由於靜態成員會在加載類的時候進行初始化,因此這就變相的要求 AsyncTask 的類必須在主線程中加載,否則同一個進程中的 AsyncTask 都將無法正常工作。
mHandler 接收到 MESSAGE_POST_RESULT 消息後會調用 AsyncTask 的finish 方法,如下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
從上述代碼可知,如果AsyncTask 被取消了,就調用 onCancelled 方法,否則就會調用 onPostExecute 方法,而onPostExecute 方法的參數就是 doInBackground 方法的返回值,以上就是AsyncTask 的整個工作過程。
總結:
AsyncTask 構建的時候會做以下幾件事情:
1、檢查當前任務是否已經執行,如果已經執行,則拋出異常,如果沒執行,則將當前任務的狀態置爲執行狀態,接着調用onPreExecute() 方法,用於執行後臺任務前的初始化操作。
2、構建一個IntentHandler 用於切換線程,構建兩個線程池,一個用於執行任務,一個用於給任務排隊,同時還會構建一個workRunnable 並將執行的參數封裝成一個FuterTask 任務交給線程池排隊和執行,在workRunnable 中,會調用 doInBackground 執行後臺任務,並將執行結果通過postResult 方法發送給Handler,用於更新任務執行的進度及最終的執行結果。