AsyncTask:異步任務(wù)
在前面的章節(jié)有提到過,Android 系統(tǒng)默認會在主線程(UI 線程)執(zhí)行任務(wù),但是如果有耗時程序就會阻塞 UI 線程,導(dǎo)致頁面卡頓。這時候我們通常會將耗時任務(wù)放在獨立的線程,然后通過 Handler 等線程間通信機制完成 UI 的刷新。很多時候我們也許只是想執(zhí)行一個簡單的任務(wù),為此寫一套 Handler 線程通信就會顯得比較復(fù)雜,不用擔(dān)心,Android 系統(tǒng)為我們提供了一個專門用于執(zhí)行異步任務(wù)的工具——Async Task,它可以讓我們執(zhí)行后臺任務(wù)并輕松的與 UI 線程進行狀態(tài)同步,今天就一起來學(xué)習(xí)一下 AyncTask 的用法。
1. AsyncTask 簡介
AsyncTask 類通常用來在執(zhí)行后臺任務(wù)的同時刷新 UI,通過調(diào)用execute()
方法觸發(fā)后臺任務(wù)的執(zhí)行,首先會回調(diào) AsyncTask 的onPreExecute()
,接著回調(diào)doInBackground()
來執(zhí)行自定義的后臺任務(wù),最后回調(diào)onPostExecute()
方法用來刷新 UI,整體流程示意圖如下:
2. AsyncTask 的基本用法
2.1 聲明 AsyncTask
我們不能直接創(chuàng)建 AsyncTask,正確的方法是繼承自 AsyncTask 實現(xiàn)一個它的子類,如下:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
// 執(zhí)行后臺耗時任務(wù)
return;
}
protected void onProgressUpdate(Integer... progress) {
// 任務(wù)執(zhí)行進度更新
}
protected void onPostExecute(Long result) {
// 執(zhí)行完畢,更新UI
}
}
以上是 AsyncTask 的核心回調(diào)方法,每個方法的含義會在后面具體講到。
2.2 指定參數(shù)
AsyncTask 可以幫助你在子線程和主線程之間同步參數(shù),根據(jù)不同的業(yè)務(wù)場景,參數(shù)類型和個數(shù)也會不一樣。剛剛在 2.1 小節(jié)聲明 AyncTask 子類的時候,需要傳入 3 個泛型參數(shù):
-
TypeOfVarArgParams:
在任務(wù)啟動之后,傳入給后臺任務(wù)的參數(shù)類型 -
ProgressValue:
啟動之后到任務(wù)結(jié)束之間,系統(tǒng)會不斷回調(diào)此方法,用來更新任務(wù)的進度 -
** ResultValue:**
后臺任務(wù)的執(zhí)行結(jié)果
2.3 啟動后臺任務(wù)
在聲明完后臺任務(wù)之后,就可以直接啟動了。啟動方式比較簡單,直接通過調(diào)用execute()
方法啟動后臺任務(wù):
new DownloadFilesTask().execute(url1, url2, url3);
3 AsyncTask 關(guān)鍵回調(diào)方法
AsyncTask 是由 4 個回調(diào)方法配合組成,這 4 個回調(diào)方法按照一定的順序依次被調(diào)用,所以我們需要 focus 的是在正確的回調(diào)方法中實現(xiàn)我們想要的邏輯,下面詳細看看如何使用。
- onPreExecute():
在執(zhí)行execute()
方法之后該方法立即被調(diào)用,標志著 AyncTask 正式開啟。通常用來做一些需要在后臺任務(wù)開啟之前完成的初始化工作,比如展示一個進度條、或者彈出一個對話框等等。該方法在 UI 線程執(zhí)行。 - doInBackground(Params):
在執(zhí)行完onPreExecute()
方法之后立即被調(diào)用,用來執(zhí)行需要放在后臺執(zhí)行的耗時任務(wù)。在創(chuàng)建 AsyncTask 的時候傳入的參數(shù)就是提供給doInBackground
使用的。在后臺任務(wù)執(zhí)行完畢后,還需要將執(zhí)行結(jié)果返回到onPostExecutes ()
中,同時我們也可以通過publishProgress(Progress…)
方法來手動發(fā)布任務(wù)進度,進度將從子線程發(fā)送到 UI 線程。毋庸置疑,該方法在子線程中執(zhí)行。 - onProgressUpdate(Progress…):
當(dāng)我們通過publishProgress(Params)
發(fā)布進度之后,系統(tǒng)會回調(diào)該方法,用來獲取任務(wù)執(zhí)行進度并更新 UI。這一步就完成了子線程到主線程的通信,該方法在 UI 線程執(zhí)行 - onPostExecute(Result):
當(dāng)后臺任務(wù)執(zhí)行完畢,該方法被回調(diào),同時標志著整個 AyncTask 結(jié)束。與onPreExecute
相反,通常會在onPostExecute
中做一些回收工作,比如提示“下載完成”、“加載失敗”、隱藏進度條等等。
4 AsyncTask 示例
本節(jié)的功能和第 38 節(jié) Handler 的功能類似,我們還是完成一個后臺執(zhí)行耗時任務(wù),并同步更新進度條的功能,可以直接在 Handler 的例子上修改。
4.1 布局文件
布局文件基本一樣,只是默認將進度條和進度顯示隱藏起來,等到onPreExecute()
方法的時候在做初始化展示,代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:visibility="gone"
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<Button
android:id="@+id/start_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/progressBar"
android:layout_alignParentStart="true"
android:layout_marginStart="24dp"
android:layout_marginTop="62dp"
android:text="開始任務(wù)" />
<TextView
android:visibility="gone"
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/start_progress"
android:layout_alignBottom="@+id/start_progress"
android:layout_alignParentEnd="true"
android:layout_marginEnd="85dp"
android:gravity="center"
android:text="當(dāng)前進度:0%"
android:textSize="16sp" />
</RelativeLayout>
4.2 異步任務(wù)控制
異步任務(wù)的代碼基本上和 Handler 一致,我們通過Thread.sleep(1000)
來模擬一秒鐘的耗時操作,然后在onPreExecute()
中展示進度條,在后臺任務(wù)執(zhí)行完后通過publishProgress(int)
來將任務(wù)進度發(fā)送到主線程,由onProgressUpdate(int)
方法在 UI 線程接收進度,并更新進度條。最后在結(jié)束的時候返回“已完成”提示,在onPostExecute(String)
方法中進行收尾工作,隱藏進度條并展示“已完成”。代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private static final int MAX = 100;
private ProgressBar progressBar;
private Button startProgress;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
startProgress = findViewById(R.id.start_progress);
textView = findViewById(R.id.textView);
progressBar.setMax(MAX);
startProgress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new DownloadTask().execute();
}
});
}
// 1、創(chuàng)建Async Task子類
private class DownloadTask extends AsyncTask<Integer, Integer, String> {
// 2、初始化階段,展示進度條
@Override
protected void onPreExecute() {
progressBar.setVisibility(View.VISIBLE);
textView.setVisibility(View.VISIBLE);
}
// 3、執(zhí)行后臺任務(wù)
@Override
protected String doInBackground(Integer... integers) {
int i;
for (i = 0; i < 100; i++) {
try {
// 一秒鐘的耗時操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 4、發(fā)布進度
publishProgress(i);
}
return "異步任務(wù)已完成";
}
// 5、接收后臺任務(wù)數(shù)據(jù)并更新進度條
@Override
protected void onProgressUpdate(Integer... values) {
textView.setText("當(dāng)前進度:" + values[0] + "%");
progressBar.setProgress(values[0]);
}
// 6、任務(wù)結(jié)束
@Override
protected void onPostExecute(String s) {
progressBar.setVisibility(View.GONE);
textView.setText(s);
}
}
}
編譯之后,效果如下:
可以看到大體的效果和 Handler 是一樣的,但是整個異步任務(wù)的處理流程更加清晰了,而且可以很方便的做任務(wù)前的初始化及任務(wù)后的回收工作,整個線程的切換也不再需要我們?nèi)タ刂?,全部交給 AsyncTask 操作就行,一切就是這么簡單!
5 小結(jié)
本節(jié)介紹了一個專門用于執(zhí)行異步任務(wù)的工具類,首先需要創(chuàng)建一個子類繼承自 AsyncTask,然后確定好需要傳入和返回的參數(shù)類型,接著覆寫 4 個關(guān)鍵的方法,剩下的線程切換及數(shù)據(jù)傳輸就交給系統(tǒng)完成了。
相比 Handler,AsyncTask使用更簡單,我們只需要關(guān)注我們的后臺邏輯,而 Handler 需要我們自行管理的東西比較多。所以在執(zhí)行后臺任務(wù)刷新 UI 的場景,強烈推薦使用 AsyncTask,可以讓我們的開發(fā)更加順暢。