Android AsyncTask
AsyncTask 簡介
AsyncTask 實際上是一個幫助類,可以讓我們很簡單的從子執行緒切換到主執行緒,去更新UI 介面,而我們卻又可以在 doInBackground() 方法中非同步執行耗時任務。這樣的話,我們就不需要頻繁的手動切換執行緒去更新UI了。但是需要注意的是,AsyncTask 類設計的時候僅僅是針對一些從執行到結束至多幾秒的任務。為什麼會這樣呢?因為 AsyncTask 並不是為每一 AsyncTask 例項單獨建立一個執行緒。相反的,它使用 Executor 在單一的後臺程序上執行所有 AsyncTask 的後臺任務。也就是說,每個 AsyncTask 任務需要排隊執行,所以如果一個長時間執行的 AsyncTask 會阻塞其他 AsyncTask 任務。如果想要長時間的任務,推薦使用 java.util.concurrent 包中提供的一些 api,如 Executor, ThreadPoolExecutor and FutureTask.
AsyncTask 的三個引數
如果我們想要自定義一個AsyncTask,那我們必須這樣定義
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
可見,AysncTask 中包含了三個引數,下面來詳細介紹
1.Params : 這個引數是規定 doInBackground(Params)方法中Params資料型別 的,當然,如果不需要傳遞任何資料,可以設定為 Void,如果需要傳遞資料,那麼在執行時需要在 execute() 方法中輸入引數。
示例 :
AsyncTask<String,Void,Void> task = new AsyncTask<String,Void,Void>() { @Override protected Void doInBackground(String... params) { for (String parameter : params){ Log.d("MainActivity", parameter); } return null; } }; task.execute("Hello","Android");
2.Progress : 這個引數規定 onProgressUpdate(Progress) 方法中的引數型別的,當然如果不需要,可以設定為 Void
示例 :
AsyncTask<Integer,Integer,Void> task = new AsyncTask<Integer, Integer, Void>() { @Override protected Void doInBackground(Integer... integers) { for (Integer progress : integers){ publishProgress(progress); } return null; } @Override protected void onProgressUpdate(Integer... values) { int progress = values[0]; progressBar.setProgress(progress); } }; task.execute(0,25,50,75,100);
3.Result : 這個引數規定 doInBackground() 方法的資料返回型別,就不再多說。
AsyncTask 中主要的函式
- onPreExecute()
該方法是 AysncTask 執行的第一個方法, 可以用於初始化UI,比如說 顯示一個進度條等。
- doInBackground (Params… params)
這個方法所有程式碼都會在一個子執行緒中執行,所有的耗時任務都在這處理,處理完之後通過 return 將結果返回。在這個方法中如果需要進行更新 UI,可以通過 publishProgress(Progress… values)方法,這個方法會呼叫onProgressUpdate(Progress… values)去進行更新。
- onProgressUpdate (Progress… values)
這個方法在 publishProgress() 方法呼叫時呼叫,用於 更新 UI 介面。
- publishProgress (Progress… values)
注意這個方法被設計為終極方法,即 final 修飾的方法,所以不可在子類中被複寫,我們只需要呼叫該函式觸發 onProgressUpdate() 函式,完成一些 UI 介面上的操作。
- onPostExecute(Result result)
doInBackground 執行return語句後,就會轉到該方法上來,主要用於處理一些善後工作,比如關閉進度條。
AsyncTask 的工作模式
通過AysncTask.cancel(boolean) 方法,可以撤銷正在執行的 AysncTask,不過我們可以選擇溫和 和 粗暴兩種方式。
- AysncTask.cancel(false)
這個方法呼叫之後,會將 isCancelled() 狀態設定為 true,所以只要我們在doInBackground() 方法中頻繁的檢查這個狀態,就可以儘可能快的結束執行。
- AysncTask.cancel(true)
該方法會直接終止 doInBackground() 方法中的所有執行緒。
使用 AsyncTask 的注意事項
- AysncTask 只能在 UI 執行緒中載入並且例項化。
- AsyncTask.execute() 必須在UI執行緒中呼叫。
- 不要手動呼叫onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…)等函式。
- 一個任務只能被執行一次,如果兩次呼叫 execute() ,系統會丟擲一個Exception。
- AysncTask 中還有名為 onCancelled() 的函式,這個函式的觸發條件為AsyncTask.cancel() 被呼叫 且 doInBackground() 執行完畢。這也就意味著,當AsyncTask.cancel() 呼叫後,執行完doInBackground() 就會執行 onCancelled(),而不是 onPostExecute()。
使用AsyncTask
public class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
private Context mContext;
private ProgressDialog dialog;
public DownloadTask(Context context){
mContext = context;
}
@Override
protected void onPreExecute() {
dialog = new ProgressDialog(mContext);
dialog.setTitle("Downloading");
dialog.setMax(100);
dialog.show();
}
@Override
protected Boolean doInBackground(Void... voids) {
try {
int percent = 0;
while(percent <= 100){
publishProgress(percent);
percent += 25;
Thread.sleep(1000);
}
}catch (Exception e){
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
dialog.setMessage(values[0]+"%");
}
@Override
protected void onPostExecute(Boolean aBoolean) {
dialog.dismiss();
if (aBoolean){
Toast.makeText(mContext, "Download success", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(mContext, "Download failed", Toast.LENGTH_SHORT).show();
}
}
public class MainActivity extends AppCompatActivity {
private DownloadTask mDownloadTask;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mDownloadTask = new DownloadTask(this);
mButton = (Button) findViewById(R.id.start);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mDownloadTask.execute();
}
});
}