官方概述
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.
翻譯:AsyncTask可以恰當(dāng)方便的操作UI線程。它可以執(zhí)行后臺(tái)任務(wù),并把任務(wù)結(jié)果發(fā)布到UI線程,而且不需要手動(dòng)的操作thread和handler。
AsyncTask并不是一個(gè)通常的線程框架,而是為Thread和Handler設(shè)計(jì)的幫助類。AsyncTask最適合用于耗時(shí)較短(最多幾秒鐘)的操作。如果需要在線程中執(zhí)行長(zhǎng)時(shí)間的操作,強(qiáng)烈推薦使用java.util.concurrent包提供的API,比如Executor,TreadPoolExecutor和FutureTask。
一個(gè)AsyncTask可以把操作的執(zhí)行過程放到子線程中執(zhí)行,把執(zhí)行結(jié)果發(fā)布到UI線程。定義一個(gè)AsyncTask需要指定Params,Progress,Result三個(gè)泛型的類型,在執(zhí)行過程中將調(diào)用onPreExecute,doInBackground, onProgressUpdate 和 onPostExecute四個(gè)方法。
使用
AsyncTask為抽象類,使用前必須先定義一個(gè)它的子類,實(shí)現(xiàn)doInBackground()方法,為Params,Progress,Result指定確切的參數(shù)。
實(shí)現(xiàn)說明
Type | 說明 |
---|---|
Params | 任務(wù)所需參數(shù)的類型,變長(zhǎng)參數(shù);在調(diào)用任務(wù)執(zhí)行方法execute()時(shí),作為execute的參數(shù)使用。若不使用,指定為Void |
Progress | 說明任務(wù)執(zhí)行的進(jìn)度的類型,如Integer等;若不使用,指定為Void |
Result | 任務(wù)完成后返回的結(jié)果的類型,若不使用,指定為Void |
方法調(diào)用順序
順序 | 方法 | 運(yùn)行位置 | 必須實(shí)現(xiàn) | 作用 |
---|---|---|---|---|
1.Task執(zhí)行前 | onPreExecute() | UI Thread | 否 | 在Task執(zhí)行前調(diào)用(即,doInBackground()法前),可用于做一些任務(wù)執(zhí)行前的操作,如顯示一個(gè)ProgressDialog說明進(jìn)度 |
2.Task執(zhí)行中 | doInBackground(Params...) | background Thread | 是 | 用于執(zhí)行Task,在此方法中,可以調(diào)用publishProgress(Progress...) 發(fā)布Task實(shí)時(shí)進(jìn)度信息,在onProgressUpdate(Progress...)接收到發(fā)布的信息 |
3.Task執(zhí)行中 | onProgressUpdate(Progress...) | UI Thread | 否 | 接收publishProgress(Progress...)發(fā)布的Task進(jìn)度信息,可以在這里更新顯示進(jìn)度的界面, |
4.Task執(zhí)行完成后 | onPostExecute(Result) | UI Thread | 否 | 用于接收Task執(zhí)行的結(jié)果,在UI thread中執(zhí)行 |
官方提供的示例
//定義一個(gè)AysncTask類的子類
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
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));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
//調(diào)用方式
new DownloadFilesTask().execute(url1, url2, url3);
在這個(gè)示例中,定義了一個(gè)AsyncTask的子類DownloadFilesTask,其中URL對(duì)應(yīng)Params,Integer表示進(jìn)度Progress,Long為執(zhí)行結(jié)果Result的類型。
doInBackground()方法體的代碼將在子線程中執(zhí)行。
publishProgress()方法用來發(fā)布任務(wù)執(zhí)行進(jìn)度情況,此方法被調(diào)用后,會(huì)喚起在UI線程中執(zhí)行的onProgresssUpdate()方法,并接收publishProgress()發(fā)布的信息,我們可以根據(jù)這些信息更新我們的view等。
當(dāng)后臺(tái)任務(wù)執(zhí)行完成后,onPostExecute()方法會(huì)被調(diào)用,此方法也是在UI線程中執(zhí)行。
使用注意事項(xiàng)
- AsyncTask類必須加載在UI thread。(Android系統(tǒng)會(huì)自動(dòng)完成,不需要管)
- AsyncTask類的必須在UI thread中實(shí)例化。(因?yàn)锳syncTask中的Handler必須屬于UI thread,AsyncTask才能和UI thread中執(zhí)行)
- execute(Params...) 必須在UI thread中被調(diào)用。
- 不要手動(dòng)調(diào)用 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 方法,這些方法的調(diào)用由AsyncTask自定完成
- 一個(gè)AsyncTask的實(shí)例只能執(zhí)行一次,即,execute(Params...)方法只能調(diào)用一次
取消Task
官方說明:
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of **onPostExecute(Object) **will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
翻譯:你可以在任何時(shí)候通過調(diào)用cancel(boolean)取消Task,cancel(boolean)方法被調(diào)用后,調(diào)用isCancelled()方法將返回True,并且在doInBackground(Object[])返回后,不在調(diào)用onPostExecute(Object) ,而是調(diào)用 onCancelled(Object)。為了保證Task盡可能快的被取消掉,你應(yīng)該在doInBackground(Object[])方法中不停的查詢 isCancelled() 的返回值,最好在方法中實(shí)現(xiàn)一個(gè)循環(huán)。
我們來看一下cancel(boolean)的源碼
private final AtomicBoolean mCancelled = new AtomicBoolean();
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
cancle(boolean)只是將mCancelled的值置為true,而任是真正被取消的操作,要在doInBackground(Params)方法中通過查詢mCancelled的值為true時(shí)才進(jìn)行取消。
官方指出最好在doInBackground(Object[])中實(shí)現(xiàn)一個(gè)循環(huán),用來保證盡快收到任務(wù)取消的信息。代碼如下:
@Override
protected Void doInBackground(Integer... params) {
while (isCancelled()){
//do something
}
return null;
}
注意
但是有時(shí)候,我們無法使用循環(huán),比如讀取文件、請(qǐng)求網(wǎng)絡(luò)等。如果你調(diào)用了cancel(false),任務(wù)會(huì)繼續(xù)執(zhí)行,直到任務(wù)完成,但onPostExecute()將不被會(huì)被調(diào)用,而是調(diào)用onCancel()。但我們的初衷是想讓任務(wù)馬上停止,而不是繼續(xù)執(zhí)行到完成,所以我們的程序做了無用功。如果使用cancel(true) 方法,即,參數(shù)mayInterruptIfRunning被置為true,那將提前中斷我們的任務(wù),但是如果我們的方法是不可中斷的,例如BitmapFactory.decodeStream()方法,那么任務(wù)將繼續(xù)執(zhí)行。你可以過早的關(guān)閉這個(gè)流(stream)并捕獲它拋出的異常,但這并不是通過調(diào)用cancel(true)完成的,因此cancel(true)就變的沒有意義了。
執(zhí)行順序:多個(gè)AsyncTask實(shí)例之間的執(zhí)行順序
在Android 1.6之前AsyncTask是順序執(zhí)行的,在一個(gè)App中,只有前一個(gè)Task執(zhí)行完成后, 下一個(gè)AsyncTask才能執(zhí)行。
在Android 1.6-Android2.3中,由一個(gè)線程池來管理多個(gè)線程,同一時(shí)刻,可以有多個(gè)Task在執(zhí)行。
從Android3.0開始,Google為了避免并行執(zhí)行給多個(gè)應(yīng)用帶來的錯(cuò)誤,有改為了順序執(zhí)行。
版本 | 執(zhí)行順序 |
---|---|
Android 1.6之前 | 順序執(zhí)行: |
Android 1.6-Android2.3 | 并行 |
Android 3.0-至今 | 順序執(zhí)行 |
當(dāng)然我們可以通過調(diào)用 executeOnExecutor(java.util.concurrent.Executor, Params...)而非execute(Params..)方法來完成并行執(zhí)行。
比如:
aAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
可能發(fā)生的內(nèi)存泄露(Memory leaks)
我們可能有這樣的錯(cuò)覺:當(dāng)Activity被銷毀時(shí),其創(chuàng)建的AsyncTask也同樣會(huì)被銷毀------其實(shí),并不然!
當(dāng)Activity銷毀后,AsyncTask繼續(xù)執(zhí)行直到完成。如果你在Activity#onDestroy()中調(diào)用了 cancel(boolean)方法,它會(huì)繼續(xù)執(zhí)行onCancelled(Result result)方法,如果你沒有調(diào)用 cancel(boolean)方法,它會(huì)執(zhí)行onPostExecute(Result) 方法。
假如在Activity被銷毀前,我們沒有調(diào)用AsyncTask#cancel(boolean)方法。這可能引起內(nèi)存泄露,導(dǎo)致程序崩潰。比如你在onPostExecute(Result) 中對(duì)Activity的View進(jìn)行操作,但是這些View已經(jīng)隨著Activity的銷毀不再存在。所以,我們要確保在Activity銷毀前,取消掉Task。
因?yàn)楫惒饺蝿?wù)有方法在background Thread 中執(zhí)行(doInBackground()),并且也有方法在UI線程中執(zhí)行(比如,onPostExecute());在Task運(yùn)行階段,它將持有Activity的引用。但是如果Activity已經(jīng)被銷毀,它仍然持有這個(gè)的內(nèi)存的引用。如何在后續(xù)的操作中,使用這個(gè)已經(jīng)銷毀的Activity的引用,將直接導(dǎo)致程序崩潰。