AsyncTask是android提供的輕量級的異步類,可以直接繼承AsyncTask,在類中實現異步操作,並提供接口反饋當前異步執行的程度(可以通過接口實現UI進度更新),最後反饋執行的結果給UI主線程。
一、簡介
1.AsyncTask的泛型參數
public abstract class AsyncTask<Params, Progress, Result>
其中,三個泛型類型參數的含義如下:
- Params:開始異步任務執行時傳入的參數類型;
- Progress:異步任務執行過程中,返回下載進度值的類型;
- Result:異步任務執行完成後,返回的結果類型;
如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void來代替。
2.AsyncTask的核心方法
onPreExecute()
這個方法會在後臺任務開始執行之間調用,在主線程執行。用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。
doInBackground(Params...)
這個方法中的所有代碼都會在子線程中運行,我們應該在這裏去處理所有的耗時任務。
任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。
onProgressUpdate(Progress...)
當在後臺任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後臺任務中傳遞過來的。在這個方法中可以對UI進行操作,在主線程中進行,利用參數中的數值就可以對界面元素進行相應的更新。
onPostExecute(Result)
當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作爲參數傳遞到此方法中,可以利用返回的數據來進行一些UI操作,在主線程中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。
onCancelled()
在主線程中執行,當異步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用,但是要注意的是,AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務爲取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記爲中斷,需要在線程內部進行標記判斷然後中斷線程。
3.簡單使用
class DownloadTask extends AsyncTask<Integer, Integer, String>{
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(Integer... params) {
for(int i=0;i<=100;i++){
publishProgress(i);
try {
Thread.sleep(params[0]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "執行完畢";
}
@Override
protected void onProgressUpdate(Integer... progress) {
//這個函數在doInBackground調用publishProgress時觸發,雖然調用時只有一個參數
//但是這裏取到的是一個數組,所以要用progesss[0]來取值
//第n個參數就用progress[n]來取值
tv.setText(progress[0]+"%");
super.onProgressUpdate(progress);
}
@Override
protected void onPostExecute(String result) {
//doInBackground返回時觸發,換句話說,就是doInBackground執行完後觸發
//這裏的result就是上面doInBackground執行後的返回值,所以這裏是"執行完畢"
setTitle(result);
super.onPostExecute(result);
}
}
需要在主線程啓動
new DownloadTask(1).execute();
二、源碼分析
構造函數
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
初始化了兩個變量,mWorker和mFuture,並在初始化mFuture的時候將mWorker作爲參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象。 FutureTask實現了Runnable接口。
mWorker中的call()方法執行了耗時操作,即result = doInBackground(mParams);,然後把執行得到的結果通過postResult(result);,傳遞給內部的Handler跳轉到主線程中。在這裏這是實例化了兩個變量,並沒有開啓執行任務。
execute()方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
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;
}
可以 看出,先執行了onPreExecute()方法,然後具體執行耗時任務是在exec.execute(mFuture),把構造函數中實例化的mFuture傳遞進去了。該方法執行過後會把任務的執行狀態賦值爲RUNNING,如果執行任務狀態爲RUNNING的話就會報錯,也就是說一個實例不能執行兩次。
SerialExecutor類
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor 內部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務是串行執行的,即多個任務需要一個個添加到該隊列中,然後執行完隊列頭部的再執行下一個,以此類推。因爲SerialExecutor 是個靜態內部類,是所有實例化的AsyncTask對象公有的,所以說多個地方提交異步任務執行其實不會並行執行而是串行執行。
在這個方法中,有兩個主要步驟。
- 向隊列中加入一個新的任務,即之前實例化後的mFuture對象。
- 調用 scheduleNext()方法,調用THREAD_POOL_EXECUTOR執行隊列頭部的任務。
由此可見SerialExecutor 類僅僅爲了保持任務執行是串行的,實際執行交給了THREAD_POOL_EXECUTOR。
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
看名字也知道這是一個線程池,開啓了一定數量的核心線程和工作線程,並允許核心線程超時回收
postResult()
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
把結果通過handler發送
private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
AsyncTask中實例化的Handler是靜態內部類實現的,無論實例化多少AsyncTask對象用的都是一個Handler。並且爲了可以把結果返回到主線程,要求AsyncTask必須在主線程實例化。
在handlerMessage的處理中將消息分爲兩類,一類是結果,走finish()方法,一類是進度,回調onProgressUpdate()
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
在finish()中又分成了兩類,一類是任務取消,回調onCancelled(),一類是任務完成,回調onPostExecute()
三、注意事項
1.生命週期
AsyncTask不與任何組件綁定生命週期,所以在Activity或者Fragment中創建執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);
2.內存泄漏
如果AsyncTask被聲明爲Activity的非靜態的內部類,那麼AsyncTask會保留一個對創建了AsyncTask的Activity的引用。如果Activity已經被銷燬,AsyncTask的後臺線程還在執行,它將繼續在內存裏保留這個引用,導致Activity無法被回收,引起內存泄露。
3. 結果丟失
屏幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。
4.AsyncTask並行執行
從Android3.0開始,我們可以通過直接調用AsyncTask方法的executeOnExecutor方法傳入自己定義的線程池,你也可以傳入AsyncTask內部已經定義的THREAD_POOL_EXECUTOR線程池。
四、總結
使用 AsyncTask是創建AsyncTask對象之後執行execute,創建AsyncTask對象的時候會同時創建一個WorkerRunnable 對象,並且以這個WorkerRunnable對象爲參數會創建一個FutureTask對象,那麼分析AsyncTask的原理就該從execute方 法開始了,執行execute方法首先會執行executeOnExecutor方法,並且傳入一個SerialExecutor類型的對 象,SerialExecutor是一個串行線程池,一個線程裏面的所有AsyncTask全部都在這個串行的線程池中排隊執行,在 executeOnExecutor裏面首先會執行onPreExecute方法,該方法是在我們創建AsyncTask對象的時候自己實現的,運行在主 線程中,我們可以在這個方法裏面進行任務開始的提示性操作,接着線程池開始執行,也就是從這一步開始切換到了子線程中,傳入的對象就是我們創建 AsyncTask對象的時候生成的FutureTask對象,在SerialExecutor線程池的execute方法中首先會把當前 FutureTask對象插入到任務隊列中,如果當前任務隊列中沒有正在活動的AsyncTask任務的話,則會執行scheduleNext方法從隊列 中取得一個AsyncTask任務,同時當一個AsyncTask任務執行結束之後會在finally中調用scheduleNext方法執行任務隊列中 的下一個AsyncTask任務,從這裏也看出來默認情況下AsyncTask是串行執行的,真正的執行操作就該在scheduleNext方法裏面 了,這個方法裏面真正執行任務的線程池是THREAD_POOL_EXECUTOR,SerialExecutor線程池是是用來任務排隊的,保證默認情況下的串行執行,而THREAD_POOL_EXECUTOR纔是真正的任務執行者,此外在 AsyncTask裏面還有一個InternalHandler對象,其實他就是一個Handler對象而已,他存在的作用就是爲了從子線程切換到主線程 中,爲了便於在子線程執行的過程中進行一些與界面元素的交互過程,比如下載進度條的更新等等,那麼也就必須要求該InternalHandler對象在主線程中創建了,查看源碼發現InternalHandler對象是static的,也就是在AsyncTask對象創建的時候他就會創建,因此只要保證AsyncTask對象在主線程中創建就可以了,因此使用AsyncTask的時候一定要注意在主線程中創建他的對象,THREAD_POOL_EXECUTOR會執行他的execute方法,該方法實際上執行的是FutureTask的run方法,而 FutureTask的run方法實際上執行的是創建FutureTask對象的時候傳入的參數WorkerRunnable對象的call方法,查看 call方法可以看到執行了doInBackground方法,該方法也是需要我們在創建AsyncTask對象的時候自己實現的,我們可以在這個方法裏 面執行一些比較耗時的操作,它運行在子線程中,在該方法中我們可以通過publishProgress來發送一些耗時任務已經處理的進度信息,該方法運行在子線程中,該方法中會通過InternalHandler將進度消息發送出去,接着在InternalHandler裏面的handleMessage 裏面會發現是通過onProgressUpdate進行消息處理的,該方法運行在主線程中,可以進行更新進度條的一些操作,在 doInBackground方法執行結束後會將返回結果作爲參數傳遞給postResult方法,該方法同樣會通過InternalHandler發送 消息,最後在InternalHandler裏面的handleMessage裏面處理該消息,調用的是finish方法,也就是將線程切換到了主線程中 了,在finish方法中會根據主線程有沒有被暫停來執行onCancelled或者onPostExecute方法,這兩個方法是運行在主線程的,到這 裏AsyncTask的執行結束了;