網(wǎng)絡(luò)編程之AsyncTask使用總結(jié)

官方概述

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)的操作threadhandler
AsyncTask并不是一個(gè)通常的線程框架,而是為Thread和Handler設(shè)計(jì)的幫助類。AsyncTask最適合用于耗時(shí)較短(最多幾秒鐘)的操作。如果需要在線程中執(zhí)行長(zhǎng)時(shí)間的操作,強(qiáng)烈推薦使用java.util.concurrent包提供的API,比如Executor,TreadPoolExecutorFutureTask
一個(gè)AsyncTask可以把操作的執(zhí)行過程放到子線程中執(zhí)行,把執(zhí)行結(jié)果發(fā)布到UI線程。定義一個(gè)AsyncTask需要指定Params,Progress,Result三個(gè)泛型的類型,在執(zhí)行過程中將調(diào)用onPreExecute,doInBackground, onProgressUpdateonPostExecute四個(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)
  1. AsyncTask類必須加載在UI thread。(Android系統(tǒng)會(huì)自動(dòng)完成,不需要管)
  2. AsyncTask類的必須在UI thread中實(shí)例化。(因?yàn)锳syncTask中的Handler必須屬于UI thread,AsyncTask才能和UI thread中執(zhí)行)
  3. execute(Params...) 必須在UI thread中被調(diào)用。
  4. 不要手動(dòng)調(diào)用 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 方法,這些方法的調(diào)用由AsyncTask自定完成
  5. 一個(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)致程序崩潰。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評(píng)論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評(píng)論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評(píng)論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評(píng)論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評(píng)論 3 392
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容