線程通信之AsyncTask
Handler消息的處理機制,就是Android的異步消息處理機制的核心思想,一條消息通過這樣一個流程,從子線程進入到主線程,從不能更新UI變成了可以更新UI。
爲了更方便我們在子線程中對UI進行操作,除了Handler+Thread的方式,Android提供了另外一些好用的工具:AsyncTask、ThreadPoolExecutor、IntentService。本片內容的主角是AsyncTask。
public abstract class AsyncTask
自定義AsyncTask,及其方法介紹:
/**
* AsyncTask可以正確輕鬆地使用UI線程。
* 此類允許您執行後臺操作並在UI線程上發佈結果,而無需操作線程和/或handlers。
*
* abstract class AsyncTask<Params, Progress, Result>
* Params:執行AsyncTask時需要傳入的參數,可用於後臺任務中使用
* Progress:後臺任務執行時,如果需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位
* Result:當任務執行完畢後,如果需要對結果進行返回,則使用這裏指定的泛型作爲返回值類型
*/
private class DownloadTask extends AsyncTask<URL, Integer, Boolean> {
/**
* 在後臺任務開始執行之前調用,用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
}
/**
* onPreExecute()方法執行後立即在後臺線程執行該方法
* 所有的耗時操作應該這這裏執行,任務完成可以通過return語句返回執行後的結果,
* 也可以不返回(第三個泛型參數應爲Void)
* <p>
* 注意:該方法中不可以進行UI操作
*
* @param params
* @return
*/
@Override
protected Boolean doInBackground(URL... params) {
/**
* 反饋當前任務的執行速度
* 可以從#doInBackground調用此方法,以便在後臺計算仍在運行時在UI線程上發佈更新。
* 每次調用此方法將觸發在UI線程上執行#onProgressUpdate。
*/
publishProgress();
return false;
}
/**
* 在後臺任務中調用了publishProgress()方法後,會很快調用該方法
* 方法中攜帶的參數就是後臺任務中傳遞過來的,在該方法中可以對UI進行操作,利用參數值對UI進行相應更新
*
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
/**
* 後臺任務(doInBackground)執行完畢並通過return語句進行返回時,調用該方法。
* 返回的數據作爲參數傳入該方法,利用返回的數據進行一些UI操作,比如提醒任務執行的結果,
* 以及關掉進度條對話框等
*
* @param b 在{@link #doInBackground(Object [])}中計算的結果(如果有的話),可以爲null
*/
@Override
protected void onPostExecute(Boolean b) {
super.onPostExecute(b);
}
/**
* Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.
* <p>
* 默認實現簡單地調用{@link #onCancelled()}並忽略結果。
* 如果你編寫自己的實現,不要調用super.onCancelled(result)
*
* @param b 在{@link #doInBackground(Object [])}中計算的結果(如果有的話),可以爲null
*/
@Override
protected void onCancelled(Boolean b) {
super.onCancelled(b);
}
/**
* Runs on the UI thread after {@link #cancel(boolean)} is invoked
* and {@link #doInBackground(Object[])} has finished.
* <p>
* 應用程序應優先覆蓋{@link #onCancelled(Object)}。
* 此方法由{@link #onCancelled(Object)}的默認實現調用。
*/
@Override
protected void onCancelled() {
super.onCancelled();
}
}
對AsyncTask的操作方法
/**
* 啓動該任務
* new SampleAsyncTask().execute();
*/
execute();
/**
* 嘗試取消執行此任務。
* 如果任務已經完成,已經被取消或由於某種其他原因而無法取消,則此嘗試將失敗。
* 如果成功,並且在取消被調用時此任務尚未開始,則此任務不應該運行。
* 如果任務已經開始,那麼 mayInterruptIfRunning 參數可以確定執行該任務的線程是否應該被中斷以試圖停止任務。
*
* final boolean cancel(boolean mayInterruptIfRunning)
* mayInterruptIfRunning:如果執行該任務的線程應該被中斷,值爲true; 否則,正在進行的任務被允許完成。
* 如果任務無法取消,通常是因爲它已經正常完成,返回false;否則返回true
*/
cancel(mayInterruptIfRunning);
更多方法及詳細說明查看官方API
需要了解的內容:
異步任務的四個步驟:
當執行異步任務時,任務將通過4個步驟:
- onPreExecute()在執行任務之前在UI線程上調用。 此步驟通常用於設置任務,例如通過在用戶界面中顯示進度條。
- doInBackground(Params …),在onPreExecute()完成執行後立即在後臺線程上調用。 此步驟用於執行可能需要很長時間的後臺計算。 異步任務的參數傳遞給此步驟。 計算結果必須由此步驟返回,並將被傳遞迴最後一步。 此步驟也可以使用publishProgress(Progress …)發佈一個或多個進度單位。 這些值在UI線程上,在onProgressUpdate(Progress …)步驟中發佈。
- onProgressUpdate(Progress …),在調用publishProgress(Progress …)後在UI線程上調用。 執行時間未定義。 該方法用於在後臺計算仍在執行時在用戶界面中顯示任何形式的進度。 例如,它可以用於對進度條進行動畫處理或在文本字段中顯示日誌。
- onPostExecute(Result),在後臺計算完成後在UI線程上調用。 後臺計算的結果作爲參數傳遞給該步驟。
取消任務
任何時候都可以通過調用cancel(boolean)來取消任務。 調用此方法將導致對isCancelled()的後續調用返回true。 調用此方法後,onCancelled(Object)將在doInBackground(Object [])返回後被調用,而不是onPostExecute(Object)。 爲了儘可能快地取消任務,您應該始終從doInBackground(Object [])中定期檢查isCancelled()的返回值(如果可能的話)。
Threading rules
這個類必須遵循一些線程規則才能正常工作:
- 必須在UI線程上加載AsyncTask類。 這是從JELLY_BEAN自動完成的。
- 必須在UI線程上創建任務實例。
- execute(Params…) 必須在UI線程上調用。
- 不要手動調用 onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…) 等方法。
- 該任務只能執行一次(如果嘗試第二次執行,將拋出異常)。
實例
private class SampleAsyncTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();//顯示進度條對話框
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = i++;
publishProgress(downloadPercent);
Thread.sleep(200);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
//更新進度
progressBar.setProgress(values[0]);
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();//關閉對話框
//在這裏提示下載
if (result) {
Toast.makeText(MainActivity.this, "下載成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "下載失敗", Toast.LENGTH_SHORT).show();
}
}
}