Android的AsyncTask非同步任務淺析
實現原理
內部封裝了2個執行緒池+1個Handler(InternalHandler),1個執行緒池SerialExecutor任務排隊,一個執行緒池THREAD_POOL_EXECUTOR執行任務。
常用重寫的方法
- onPreExecute:執行在主執行緒中,可做UI更新,顯示進度條通知等。
- doInBackground:在子執行緒執行任務,接收的引數型別為AsyncTask第一個泛型,返回給onPostExecute的引數型別為AsyncTask第三個泛型。
- onProgressUpdate:執行在主執行緒中,doInBackground中呼叫publishProgress方法後即可回撥到該方法中,用於UI進度更新。接收的引數型別為AsyncTask第二個泛型。
- onPostExecute:執行在主執行緒中,doInBackground任務執行完畢後,就會回撥到該方法中。
- onCancelled:呼叫AsyncTask的cancel方法時,會回撥到該方法中,內部呼叫Thread的interrupt方法,告訴執行緒池要取消任務,Thread在合適時機取消任務。
public class MyTask extends AsyncTask<String, Double, Float> {
@Override // 子執行緒執行任務
protected Float doInBackground(String... strings) {
publishProgress(5D);
return 1f;
}
@Override // 準備執行doInBackground任務時回撥
protected void onPreExecute() {
super.onPreExecute();
}
@Override // doInBackground任務執行結束後回撥,接收的引數為doInBackground返回的值
protected void onPostExecute(Float aFloat) {
super.onPostExecute(aFloat);
}
@Override // doInBackground呼叫publishProgress會回撥到該方法中
protected void onProgressUpdate(Double... values) {
super.onProgressUpdate(values);
}
@Override // 呼叫AsyncTask的任務關閉後回撥
protected void onCancelled() {
super.onCancelled();
}
}
複製程式碼
注意事項
- Android4.1之前,AsyncTask類必須在主執行緒中載入。Android4.1之後,沒有了這個要求,ActivityThread的main方法中自動載入了AsyncTask。
- AsyncTask物件要在主執行緒建立,建立時,AsyncTask構造方法中,會拿到當前執行緒的Looper,傳給new的Handler例項,以保證Handler是在主執行緒中。
public AsyncTask(@Nullable Looper callbackLooper) {
// 建立AsyncTask物件時,若不傳外部的Handler例項,會走到這個構造中
// 拿到當前執行緒的Looper,傳給new的Handler,則Handler在當前建立AsyncTask物件的執行緒中
// 因此,若要保證Handler執行環境在主執行緒,必須要在主執行緒中建立AsyncTask物件
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
......省略無關程式碼
}
複製程式碼
- AsyncTask物件的execute方法必須在主執行緒中呼叫。execute方法有@MainThread註解。
- Android3.0之前AsyncTask調execute方法是並行執行任務,3.0之後增加了SerialExecutor執行緒池,預設選擇該執行緒池序列執行任務,若想並行執行,則直接調executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)方法即可。
MyTask myTask = new MyTask();
// 預設在SerialExecutor執行緒池中序列執行
myTask.execute("1");
// 並行執行
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "1");
複製程式碼
- 一個AsyncTask物件,只能執行一次execute方法,第二次執行就會拋異常。
@MainThread // 主執行緒中執行的方法,預設選擇SerialExecutor執行緒池序列執行任務
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread // 主執行緒中執行的方法。外部也可直接調這個方法執行任務,自己傳入執行緒池選擇是序列還是並行。
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
// 一個AsyncTask物件,若多次執行execute,走到這裡,會判斷任務在執行中或已結束時,都將拋異常
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
複製程式碼
- 記憶體洩漏問題。採用靜態的AsyncTask繼承類,若要引用外部,採用弱引用。
- 使用多個非同步操作並需要進行UI變更時,用AsyncTask會很複雜,替換Handler會更靈活。