1 AsyncTask介紹
Android中有Handler和AsyncTask來實現異步類,Android中UI的更新只能在主線程中完成,爲了不阻塞主線程,耗時的操作需要在異步類中完成。
AsyncTask是Android提供的輕量級的異步類,可以直接繼承AsyncTaask,在類中實現異步操作,並提供結構反饋當前異步執行的程度,簡單快捷,進程可控。
AsyncTask定義了三種泛型類型參數,Params, Progress和Result。
- Params 啓動任務執行的輸入參數,如Http請求的URL
- Progress 後臺任務執行的百分比
- Result 後臺執行任務最終返回的結果
使用AsyncTask來實現異步任務時,至少需要重寫以下兩個方法:
- doInBackground(Params…) 後臺執行的耗時操作,如網絡請求,注意不能在這個方法中更新UI。
- onPostExecute(Result) 這個方法是在UI線程中,主要用來在耗時操作完成後更新UI
還有其他的三個方法,但不是必須要實現的
- onProgressUpdate(Progress) 顯示任務執行的進度
- onPreExecute() 任務執行之前調用此方法,可以在這裏顯示進度對話框
- onCancelled() 用戶取消耗時操作時的操作
使用AsyncTask類實現異步操作時,需要注意一下幾點:
- Task必須在主線程中創建
- execute()方法必須在主線程中調用
- 每個task只能執行一次,多次調用會產生異常
下面是一個AsyncTask使用的例子,注意需要在主線程中調用execute()方法
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
private TextView textView;
private ProgressBar progressBar;
public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
super();
this.textView = textView;
this.progressBar = progressBar;
}
/**
* 這裏的Integer參數對應AsyncTask中的第一個參數
* 這裏的String返回值對應AsyncTask的第三個參數
* 該方法並不運行在UI線程當中,主要用於異步操作,所有在該方法中不能對UI當中的空間進行設置和修改
* 但是可以調用publishProgress方法觸發onProgressUpdate對UI進行操作
*/
@Override
protected String doInBackground(Integer... params) {
NetOperator netOperator = new NetOperator();
int i = 0;
for (i = 10; i <= 100; i+=10) {
netOperator.operator();
publishProgress(i);
}
return i + params[0].intValue() + "";
}
/**
* 這裏的String參數對應AsyncTask中的第三個參數(也就是接收doInBackground的返回值)
* 在doInBackground方法執行結束之後在運行,並且運行在UI線程當中 可以對UI空間進行設置
*/
@Override
protected void onPostExecute(String result) {
textView.setText("異步操作執行結束" + result);
}
//該方法運行在UI線程當中,並且運行在UI線程當中 可以對UI空間進行設置
@Override
protected void onPreExecute() {
textView.setText("開始執行異步線程");
}
/**
* 這裏的Intege參數對應AsyncTask中的第二個參數
* 在doInBackground方法當中,,每次調用publishProgress方法都會觸發onProgressUpdate執行
* onProgressUpdate是在UI線程中執行,所有可以對UI空間進行操作
*/
@Override
protected void onProgressUpdate(Integer... values) {
int vlaue = values[0];
progressBar.setProgress(vlaue);
}
}
2 AsyncTask代碼解析
我們進入AsyncTask類中查看這個異步類的工作工作流程,當我們在主線程中調用AsyncTask的execute()方法後。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
執行的是executeOnExecutor()方法,其中一個參數是我我們調用時的params,而sDefaultExecutor我們就不知道是什麼類型了。查課你sDefaultExecutor的定義,是一個SerialExecutor類的對象。SerialExecutor內部類定義如下:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
...
}
protected synchronized void scheduleNext() {
...
}
}
SerialExecutor類實現了Executor的接口,主要是execute()方法。我們稍後再介紹。回到前面executeOnExecutor()函數,我們看一下這個函數的定義.
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;
}
首先判斷狀態status, Status是一個枚舉類型,包含三個值:
- PENDING : 待定狀態
- RUNNING : 正在運行狀態,如果執行一個操作,而這個操作的狀態爲正在執行,會拋出異常
- FINISHED : 已完成狀態
如果狀態爲PENDING, 則會繼續執行,首先會把狀態改爲RUNNING,然後回調onPreExecute()方法。接下來我們看一下mWorker的定義。
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
//….
}
可以看到mWorker在構造方法中完成了初始化,在call方法中最終調用了我們熟悉的doInBackground()方法,並返回Result值作爲參數給postResult方法。
接下來我們分析postResult方法
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
在postResult方法中,首先調用了getHandler()方法,可以猜測這個方法得到了一個實現異步消息機制的Handlder對象,我們查看該函數
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
果然,函數返回一個Handler對象,不過對象是自定義的InternalHandler的。我們繼續查看InternalHandler類的定義
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:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
當msg.what = MESSAGE_POST_RESULT時,調用了mTask的finish方法,當msg.what = MESSAGE_POST_PROGRESS時,調用了mTask.onProgressUpdate()方法。我們分析一下finish()方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
當請求沒有被cancel時,調用了onPostExecute(result)函數,即通過handler,在主線程中改變UI的狀態。最後將狀態改爲已完成。
3 AsyncTask存在的問題
(1) 生命週期
很多人認爲一個在Activity中的AsyncTask會隨着Activity的銷燬而銷燬,然而事實並非如此,AsyncTask會一直執行doInBackground方法直到方法結束。一旦上述方法結束,會根據情況進行不同的操作:
- 如果cancel(boolean)調用了,則執行onCancelled(Resule)方法
- 如果cancel(boolean)沒有調用,則執行onPostExecute(Result)方法
(2)內存泄露
在Activiy中使用非靜態匿名內部AsyncTask類,由於java內部類的特點,AsysncTask內部類會持有外部類的隱式引用。由於AsyncTask的生命週期可能比Activity的長,當Activity銷燬時AsyncTask還在執行,而AsyncTask持有Activity的引用,導致Activity無非回收,進而產生內存泄露。