-- 做者 謝恩銘 轉載請註明出處javascript
在Android應用開發中,有時咱們須要實現任務的同步。html
Android裏的AsyncTask類能夠幫咱們更好地管理線程同步(異步方式),就像Thread類能作的,不過用法比Thread更簡單。java
AsyncTask算是幫咱們作了一層封裝吧,使咱們能夠不用操心那麼多,若是閱讀AsyncTask的源碼就能夠了解。android
具體AsyncTask的使用方法,最好參看Google Android的官方文檔:git
developer.android.com/reference/a…程序員
在你開發Android應用程序時,若是有一個耗時任務(一般是一個子線程),而且這個任務調用了主線程,應用就會拋出著名的「ANR」 (Application Not Responding,"應用無響應")錯誤。github
AsyncTask類能夠幫咱們解圍,使用AsyncTask能讓咱們正確及簡便地使用主線程,即便此時另有一個異步線程被建立。微信
AsyncTask是asynchronous(英語「異步的」的意思)和task(英語「任務」的意思)的縮寫,表示「異步任務」。多線程
它使得耗時任務能夠在後臺執行,並在前臺(UI線程,或稱主線程)把執行結果展示出來,沒必要用到Thread類或Handler類。線程間通訊也隨之變得更簡單,優雅。異步
主線程(User Interface Thread,UI線程)是在Android裏負責和用戶界面進行交互的線程。
AsyncTask是一個抽象類(abstract class),必須被繼承才能實例化。有三個泛型參數,分別是:
Params : 傳遞給執行的任務的參數,也就是doInBackground方法的參數。
Progress : 後臺任務執行過程當中在主線程展示更新時傳入的參數,也就是onProgressUpdate方法的參數。
Result : 後臺執行的任務返回的結果,也就是onPostExecute方法的參數。
除此以外,繼承AsyncTask類時,通常須要實現四個方法。
固然應用程序不須要調用這些方法,這些方法會在任務執行過程當中被自動調用: onPreExecute, doInBackground, onProgressUpdate 和 onPostExecute:
onPreExecute : 此方法在主線程中執行,用於初始化任務。
doInBackground : 此方法在後臺執行,是一個抽象方法,必需要被子類重寫。此方法在onPreExecute方法執行完後啓動。這個方法中執行的操做能夠是耗時的,並不會阻塞主線程。經過調用publishProgress方法來在主線程顯示後臺任務執行的結果更新。
onProgressUpdate : 此方法也在主線程中執行,每當publishProgress方法被調用時,此方法就被執行,此方法只在doInBackground執行過程當中才能被調用。
onPostExecute : 在doInBackground方法執行完以後啓動的方法,在後臺任務結束後才調用此方法,也在主線程執行。
爲了更好地理解AsyncTask的使用,咱們來實現一個計時器的小應用。
首先咱們建立一個Android項目,就命名爲AsyncTaskActivity好了(名字無所謂),修改 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文件中,咱們主要定義了:
一個EditText,用於輸入須要計數的時間
一個TextView,用於顯示計數的變化
一個Button,用於啓動計數任務。
在咱們的類AsyncTaskActivity中,咱們首先聲明三個private變量,對應以上三個元素。
private EditText chronoValue;
private TextView chronoText;
private Button start;複製代碼
而後建立一個內部類,繼承AsyncTask類,命名爲「Chronograph」,就是英語「秒錶,計時器」的意思。
private class Chronograph extends AsyncTask<Integer, Integer, Void> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 在計時開始前,先使按鈕和EditText不能用
chronoValue.setEnabled(false);
start.setEnabled(false);
chronoText.setText("0:0");
}
@Override
protected Void doInBackground(Integer... params) {
// 計時
for (int i = 0; i <= params[0]; i++) {
for (int j = 0; j < 60; j++) {
try {
// 發佈增量
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);
}
}複製代碼
以上,咱們重寫了咱們須要的四個方法。最後咱們再完成咱們AsyncTaskActivity類的onCreate方法:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 獲取三個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裏的數值
int value = Integer.parseInt(String.valueOf(chronoValue.getText()));
// 驗證數值是否大於零
if (value > 0) {
new Chronograph().execute(value);
}
else {
Toast.makeText(AsyncTaskActivity.this, "請輸入一個大於零的整數值 !", Toast.LENGTH_LONG).show();
}
}
});
}複製代碼
若是咱們在繼承AsyncTask類時,對於三個參數中有不須要的,能夠定義爲Void類型(注意,與小寫的 void 不一樣),例如:
private class Chronograph extends AsyncTask<Integer, Integer, Void> {...}複製代碼
運行咱們的項目,能夠獲得以下面三張圖所示的結果:
怎麼樣,AsyncTask不難使用吧~
這個例子項目我發在個人Github上了,請參看:
從此,當有異步任務須要執行時,可使用AsyncTask類,能夠根據本身的須要來定製。
AsyncTask使用了線程池(Thread Pool)的機制,使得同時執行多個AsyncTask成爲可能。可是要注意的是,這個線程池的容量是5個線程同時執行,若是超過了這個數量,多餘的線程必須等待線程池裏的線程執行完才能啓動。
使用AsyncTask,最好在明確知道任務會有一個肯定和合理的結束的狀況下。不然,仍是使用傳統的Thread類爲好。
在doInBackground方法中的耗時操做最好是能保證在幾秒鐘以內完成的,不要作特別久的耗時操做。
人世間,
萬千情感皆有溫度,
千萬代碼似有性格。
這裏有原創教程,IT叢林......
和你一塊兒探索程序人生。
微信公衆號「程序員聯盟」ProgrammerLeague
我是謝恩銘,在巴黎奮鬥的嵌入式軟件工程師。
我的簡介熱愛生活,喜歡游泳,略懂烹飪。人生格言:「向着標杆直跑」