標準的寫法
private static class MyAsyncTask extends AsyncTask<String,Integer,Boolean> {
/* 爲避免內存泄漏,引用應該用弱引用包裹 */
private String TAG = "MyAsyncTask";
private WeakReference<Context> contextWeakReference;
private WeakReference<ProgressBar> progressBarWeakReference;
private WeakReference<TextView> textViewWeakReference;
public MyAsyncTask(Context context, ProgressBar progressBar, TextView tvProgress) {
contextWeakReference = new WeakReference<>(context);
progressBarWeakReference = new WeakReference<>(progressBar);
textViewWeakReference = new WeakReference<>(tvProgress);
}
/**
* 運行在UI線程中,用於參數初始化
* 在 doInBackground 前調用
*/
@Override
protected void onPreExecute() {
Log.i(TAG,"onPreExecute...");
Context context = contextWeakReference.get();
if (context!=null)
Toast.makeText(context,"開始執行",Toast.LENGTH_SHORT).show();
}
/**
* 後臺任務處理方法,運行在
* 子線程,可以做一些耗時任務
* @param strings 輸入參數,對應於泛型:Params
* @return 結果
*/
@Override
protected Boolean doInBackground(String... strings) {
Log.i(TAG,"doInBackground...");
int i=0;
while(i<100){
if (isCancelled())
break;
i++;
//任務執行過程進度/參數的回調,對應於泛型:Progress
publishProgress(i);
try {
Thread.sleep(200);
} catch (InterruptedException ignored) {
}
}
//返回值代表最後的結果,對應於泛型:Result
return true;
}
/**
* 任務執行過程中的更新,
* 由子方法:publishProgress()
* 觸發,運行在UI線程中
* @param values 參數
*/
@Override
protected void onProgressUpdate(Integer... values) {
TextView tvProgress = textViewWeakReference.get();
ProgressBar progressBar = progressBarWeakReference.get();
if (tvProgress!=null)
tvProgress.setText(values[0]+"%");
if (progressBar!=null)
progressBar.setProgress(values[0]);
}
/**
* 執行結束的回調,運行在UI線程
* @param bool 參數
*/
@Override
protected void onPostExecute(Boolean bool) {
Log.i(TAG,"onPostExecute, " + bool);
Context context = contextWeakReference.get();
if (context!=null)
Toast.makeText(context,"執行完畢",Toast.LENGTH_SHORT).show();
}
/**
* 執行 task.cancel() 方法後,
* 會回調此方法
*/
@Override
protected void onCancelled() {
Log.i(TAG,"onCancelled");
}
}
執行
findViewById(R.id.btn).setOnClickListener(v -> {
if (myAsyncTask==null) {
myAsyncTask = new MyAsyncTask(TestAsyncTaskActivity.this,progressBar,tvProgress);
}
myAsyncTask.execute("start");
});
if (myAsyncTask!=null)
myAsyncTask.cancel(true);
總結
泛型類型要清楚,如果不需要可以填Void
如果寫成內部類,則注意內存泄漏,需要寫成靜態內部類,同時注意,如果需要刷新UI控件,那麼得持有UI控件得引用,那麼建議使用弱引用包裹,避免泄漏。
清楚每個回調方法執行的線程,以及其泛型參數的對應位置。
doInBackgroud方法中,要及時去判斷,任務是否被 cancel 了,如果Cancel了,則要立即取消這個耗時任務。假如,任務被取消後,而 doInbackgroud 中沒有及時退出,那麼Task會執行完成方法後,再進行回調 onCanceled 方法。
if (isCancelled())
break;
- 關於AsyncTask的是否並行串行,其在不同Android版本上是有差異的,具體看源碼類註釋:
* <p>When first introduced, AsyncTasks were executed serially on a single background
* thread. Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed
* to a pool of threads allowing multiple tasks to operate in parallel. Starting with
* {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
* thread to avoid common application errors caused by parallel execution.</p>
* <p>If you truly want parallel execution, you can invoke
* {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
* {@link #THREAD_POOL_EXECUTOR}.</p>
Android Version < 4 :串行;
11> Android Version >= 4 :並行;
Android Version >= 11 :默認串行,可以設置爲並行;
串行的意思就是,Task任務,在多次啓動下,會排隊執行,並行就是一併執行。