-- 作者 謝恩銘 轉(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ò)誤。
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é)果:
怎么樣坯约,AsyncTask不難使用吧~
這個(gè)例子項(xiàng)目我發(fā)在我的Github上了熊咽,請(qǐng)參看:
https://github.com/frogoscar/AsyncTaskExample
總結(jié)
今后,當(dāng)有異步任務(wù)需要執(zhí)行時(shí)闹丐,可以使用AsyncTask類(lèi)横殴,可以根據(jù)自己的需要來(lái)定制。
AsyncTask使用了線程池(Thread Pool)的機(jī)制卿拴,使得同時(shí)執(zhí)行多個(gè)AsyncTask成為可能衫仑。但是要注意的是,這個(gè)線程池的容量是5個(gè)線程同時(shí)執(zhí)行堕花,如果超過(guò)了這個(gè)數(shù)量文狱,多余的線程必須等待線程池里的線程執(zhí)行完才能啟動(dòng)。
使用AsyncTask航徙,最好在明確知道任務(wù)會(huì)有一個(gè)確定和合理的結(jié)束的情況下如贷。否則,還是使用傳統(tǒng)的Thread類(lèi)為好到踏。
在doInBackground方法中的耗時(shí)操作最好是能保證在幾秒鐘之內(nèi)完成的杠袱,不要做特別久的耗時(shí)操作。
我是謝恩銘窝稿,在巴黎奮斗的軟件工程師楣富。
熱愛(ài)生活,喜歡游泳伴榔,略懂烹飪纹蝴。
人生格言:「向著標(biāo)桿直跑」