Android探索之旅 | 用AsyncTask實(shí)現(xiàn)多線程+實(shí)例

-- 作者 謝恩銘 轉(zhuǎn)載請(qǐng)注明出處

用AsyncTask實(shí)現(xiàn)多線程


在Android應(yīng)用開(kāi)發(fā)中亏推,有時(shí)我們需要實(shí)現(xiàn)任務(wù)的同步。

Android里的AsyncTask類(lèi)可以幫我們更好地管理線程同步(異步方式)年堆,就像Thread類(lèi)能做的吞杭,不過(guò)用法比Thread更簡(jiǎn)單。

AsyncTask算是幫我們做了一層封裝吧变丧,使我們可以不用操心那么多芽狗,如果閱讀AsyncTask的源碼就可以了解。

具體AsyncTask的使用方法锄贷,最好參看Google Android的官方文檔:

https://developer.android.com/reference/android/os/AsyncTask.html

在你開(kāi)發(fā)Android應(yīng)用程序時(shí)译蒂,如果有一個(gè)耗時(shí)任務(wù)(通常是一個(gè)子線程),并且這個(gè)任務(wù)調(diào)用了主線程谊却,應(yīng)用就會(huì)拋出著名的“ANR” (Application Not Responding柔昼,"應(yīng)用無(wú)響應(yīng)")錯(cuò)誤。

ANR

AsyncTask類(lèi)可以幫我們解圍炎辨,使用AsyncTask能讓我們正確及簡(jiǎn)便地使用主線程捕透,即使此時(shí)另有一個(gè)異步線程被創(chuàng)建。

AsyncTask是asynchronous(英語(yǔ)“異步的”的意思)和task(英語(yǔ)“任務(wù)”的意思)的縮寫(xiě)碴萧,表示“異步任務(wù)”乙嘀。

它使得耗時(shí)任務(wù)可以在后臺(tái)執(zhí)行,并在前臺(tái)(UI線程破喻,或稱(chēng)主線程)把執(zhí)行結(jié)果展現(xiàn)出來(lái)虎谢,不必用到Thread類(lèi)或Handler類(lèi)。線程間通信也隨之變得更簡(jiǎn)單曹质,優(yōu)雅婴噩。

主線程(User Interface Thread擎场,UI線程)是在Android里負(fù)責(zé)和用戶界面進(jìn)行交互的線程。

AsyncTask是一個(gè)抽象類(lèi)(abstract class)几莽,必須被繼承才能實(shí)例化迅办。有三個(gè)泛型參數(shù),分別是:

  • Params : 傳遞給執(zhí)行的任務(wù)的參數(shù)章蚣,也就是doInBackground方法的參數(shù)站欺。

  • Progress : 后臺(tái)任務(wù)執(zhí)行過(guò)程中在主線程展現(xiàn)更新時(shí)傳入的參數(shù),也就是onProgressUpdate方法的參數(shù)纤垂。

  • Result : 后臺(tái)執(zhí)行的任務(wù)返回的結(jié)果矾策,也就是onPostExecute方法的參數(shù)。

除此之外洒忧,繼承AsyncTask類(lèi)時(shí)蝴韭,一般需要實(shí)現(xiàn)四個(gè)方法。

當(dāng)然應(yīng)用程序不需要調(diào)用這些方法熙侍,這些方法會(huì)在任務(wù)執(zhí)行過(guò)程中被自動(dòng)調(diào)用: onPreExecute, doInBackground, onProgressUpdate 和 onPostExecute:

  • onPreExecute : 此方法在主線程中執(zhí)行榄鉴,用于初始化任務(wù)。

  • doInBackground : 此方法在后臺(tái)執(zhí)行蛉抓,是一個(gè)抽象方法庆尘,必須要被子類(lèi)重寫(xiě)。此方法在onPreExecute方法執(zhí)行完后啟動(dòng)巷送。這個(gè)方法中執(zhí)行的操作可以是耗時(shí)的驶忌,并不會(huì)阻塞主線程。通過(guò)調(diào)用publishProgress方法來(lái)在主線程顯示后臺(tái)任務(wù)執(zhí)行的結(jié)果更新笑跛。

  • onProgressUpdate : 此方法也在主線程中執(zhí)行付魔,每當(dāng)publishProgress方法被調(diào)用時(shí),此方法就被執(zhí)行飞蹂,此方法只在doInBackground執(zhí)行過(guò)程中才能被調(diào)用几苍。

  • onPostExecute : 在doInBackground方法執(zhí)行完之后啟動(dòng)的方法,在后臺(tái)任務(wù)結(jié)束后才調(diào)用此方法陈哑,也在主線程執(zhí)行妻坝。

實(shí)例


為了更好地理解AsyncTask的使用,我們來(lái)實(shí)現(xiàn)一個(gè)計(jì)時(shí)器的小應(yīng)用惊窖。

首先我們創(chuàng)建一個(gè)Android項(xiàng)目刽宪,就命名為AsyncTaskActivity好了(名字無(wú)所謂),修改 res->layout 里的定義主用戶界面的 xml 文件(比如是main.xml):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="15dp" >
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:padding="5dp"
        android:text="Time in min"
        android:textSize="22sp"
        android:textStyle="bold" />
                                                                       
    <EditText
        android:id="@+id/chronoValue"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_marginBottom="15dp"
        android:layout_gravity="center"
        android:hint="minutes"
        android:inputType="number"
        android:maxLines="1"
        android:text="1"
        android:textSize="20sp" />

    <TextView
        android:id="@+id/chronoText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:text="0:0"
        android:textSize="80sp" />

    <Button
        android:id="@+id/start"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="15dp"
        android:text="Start" />
</LinearLayout>

在以上的main.xml文件中界酒,我們主要定義了:

  • 一個(gè)EditText圣拄,用于輸入需要計(jì)數(shù)的時(shí)間

  • 一個(gè)TextView,用于顯示計(jì)數(shù)的變化

  • 一個(gè)Button毁欣,用于啟動(dòng)計(jì)數(shù)任務(wù)庇谆。

在我們的類(lèi)AsyncTaskActivity中赁遗,我們首先聲明三個(gè)private變量,對(duì)應(yīng)以上三個(gè)元素族铆。

private EditText chronoValue;
private TextView chronoText;
private Button start;

然后創(chuàng)建一個(gè)內(nèi)部類(lèi),繼承AsyncTask類(lèi)哭尝,命名為“Chronograph”哥攘,就是英語(yǔ)“秒表,計(jì)時(shí)器”的意思材鹦。

private class Chronograph extends AsyncTask<Integer, Integer, Void> {
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        // 在計(jì)時(shí)開(kāi)始前逝淹,先使按鈕和EditText不能用
        chronoValue.setEnabled(false);
        start.setEnabled(false);
        chronoText.setText("0:0");
    }
    @Override
    protected Void doInBackground(Integer... params) {
        // 計(jì)時(shí)
        for (int i = 0; i <= params[0]; i++) {
            for (int j = 0; j < 60; j++) {
                try {
                    // 發(fā)布增量
                    publishProgress(i, j);
                    if (i == params[0]) {
                        return null;
                    }
                    // 暫停一秒
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        if (isCancelled()) {
            return null;
        }
        return null;
    }
                                                                                     
    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        // 更新UI界面
        chronoText.setText(values[0] + ":" + values[1]);
    }

    @Override 
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        // 重新使按鈕和EditText可以使用
        chronoValue.setEnabled(true);
        start.setEnabled(true);
    }
}

以上,我們重寫(xiě)了我們需要的四個(gè)方法桶唐。最后我們?cè)偻瓿晌覀傾syncTaskActivity類(lèi)的onCreate方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
                                                               
    // 獲取三個(gè)UI組件
    start = (Button)findViewById(R.id.start);
    chronoText = (TextView)findViewById(R.id.chronoText);
    chronoValue = (EditText)findViewById(R.id.chronoValue);
                                                               
    start.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            // 獲取EditText里的數(shù)值
            int value = Integer.parseInt(String.valueOf(chronoValue.getText()));
            // 驗(yàn)證數(shù)值是否大于零
            if (value > 0) {
                new Chronograph().execute(value);
            }
            else {
                Toast.makeText(AsyncTaskActivity.this, "請(qǐng)輸入一個(gè)大于零的整數(shù)值 !", Toast.LENGTH_LONG).show();
            }
        }
    });
}

如果我們?cè)诶^承AsyncTask類(lèi)時(shí)栅葡,對(duì)于三個(gè)參數(shù)中有不需要的,可以定義為Void類(lèi)型(注意尤泽,與小寫(xiě)的 void 不同)欣簇,例如:

private class Chronograph extends AsyncTask<Integer, Integer, Void> {...}

運(yùn)行我們的項(xiàng)目,可以得到如下面三張圖所示的結(jié)果:

按下Start按鈕前

計(jì)數(shù)中

計(jì)數(shù)結(jié)束后

怎么樣坯约,AsyncTask不難使用吧~

這個(gè)例子項(xiàng)目我發(fā)在我的Github上了熊咽,請(qǐng)參看:

https://github.com/frogoscar/AsyncTaskExample

總結(jié)


  1. 今后,當(dāng)有異步任務(wù)需要執(zhí)行時(shí)闹丐,可以使用AsyncTask類(lèi)横殴,可以根據(jù)自己的需要來(lái)定制。

  2. AsyncTask使用了線程池(Thread Pool)的機(jī)制卿拴,使得同時(shí)執(zhí)行多個(gè)AsyncTask成為可能衫仑。但是要注意的是,這個(gè)線程池的容量是5個(gè)線程同時(shí)執(zhí)行堕花,如果超過(guò)了這個(gè)數(shù)量文狱,多余的線程必須等待線程池里的線程執(zhí)行完才能啟動(dòng)。

  3. 使用AsyncTask航徙,最好在明確知道任務(wù)會(huì)有一個(gè)確定和合理的結(jié)束的情況下如贷。否則,還是使用傳統(tǒng)的Thread類(lèi)為好到踏。

  4. 在doInBackground方法中的耗時(shí)操作最好是能保證在幾秒鐘之內(nèi)完成的杠袱,不要做特別久的耗時(shí)操作。


我是謝恩銘窝稿,在巴黎奮斗的軟件工程師楣富。
熱愛(ài)生活,喜歡游泳伴榔,略懂烹飪纹蝴。
人生格言:「向著標(biāo)桿直跑」

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末庄萎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子塘安,更是在濱河造成了極大的恐慌糠涛,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兼犯,死亡現(xiàn)場(chǎng)離奇詭異忍捡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)切黔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)砸脊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人纬霞,你說(shuō)我怎么就攤上這事凌埂。” “怎么了诗芜?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵瞳抓,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我伏恐,道長(zhǎng)挨下,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任脐湾,我火速辦了婚禮臭笆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秤掌。我一直安慰自己愁铺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布闻鉴。 她就那樣靜靜地躺著茵乱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孟岛。 梳的紋絲不亂的頭發(fā)上瓶竭,一...
    開(kāi)封第一講書(shū)人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音渠羞,去河邊找鬼斤贰。 笑死,一個(gè)胖子當(dāng)著我的面吹牛次询,可吹牛的內(nèi)容都是我干的荧恍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼送巡!你這毒婦竟也來(lái)了摹菠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骗爆,失蹤者是張志新(化名)和其女友劉穎次氨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體摘投,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡糟需,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谷朝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡武花,死狀恐怖圆凰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情体箕,我是刑警寧澤专钉,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站累铅,受9級(jí)特大地震影響跃须,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜娃兽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一菇民、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧投储,春花似錦第练、人聲如沸榜贴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)空执。三九已至勋眯,卻和暖如春婴梧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背客蹋。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工塞蹭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讶坯。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓浮还,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親闽巩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钧舌,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,070評(píng)論 25 707
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 3,206評(píng)論 1 15
  • 在Android中我們可以通過(guò)Thread+Handler實(shí)現(xiàn)多線程通信担汤,一種經(jīng)典的使用場(chǎng)景是:在新線程中進(jìn)行耗時(shí)...
    呂侯爺閱讀 2,051評(píng)論 2 23
  • 其一 十年前,鄰居家中五歲小兒找我玩耍洼冻。無(wú)意間從抽屜里翻將出一封信崭歧,因好奇問(wèn)道,這是什么撞牢? 我說(shuō)率碾,一封信。 孩子的...
    輪回夢(mèng)里閱讀 701評(píng)論 6 2
  • 一時(shí)書(shū) 智庫(kù)捐款一元 白血病兒童捐款兩元 二時(shí)書(shū) 友善的語(yǔ)言和鄰居溝通 水道漏水屋彪,曾經(jīng)種下破壞他人財(cái)產(chǎn)的種子所宰。明智...
    我不叫許仲斌閱讀 226評(píng)論 0 0