前言
一提到多線程,我們不得不提到AsyncTask,很多Android開發人員在網絡請求這塊,一般會使用開源框架,比如Volley,Okhttp,retrofit等。但是有一部分人,比較忠於封裝AsyncTask去實現。我們都知道AsyncTask內部是Handler實現的,今天我們就來一探究竟。
基本使用
AsyncTask使用了模板方法模式,使用AsyncTask,我們需要寫一個類去繼承它,然後重寫它的幾個方法,如下:
public class MyAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
}
}
AsyncTask傳入的參數分別代表:在調用excute方法傳入的參數類型,onProgressUpdate方法的參數類型和doInBackground返回的參數類型。下面再說明下幾個方法:
a.onPreExecute:最先執行的方法,運行在主線程,一般我們做顯示Dialog等的準備工作。
b.doInBackground:在onPreExecute只後執行的方法,運行在線程池,這裏主要做耗時操作,然後將結果返回。
c.onPostExecute:最後執行的方法,運行在主線程,方法參數爲doInBackground返回的值。
d.onProgressUpdate:顯示請求任務進度的方法,運行在主線程,需要在doInBackground調用publishProgress(values)方法。比如說下載任務進度。。。
在使用的時候就直接:
new MyAsyncTask().excute();//當然也可以傳入一些參數
基本使用就差不多介紹完畢,下面我們羅列下AsyncTask需要注意的地方:
- AsyncTask必須在主線程中加載。不過在android4.1以上系統自動完成了。AsyncTask必須在主線程中創建。excute方法必須在主線程中調用。
- 一個AsyncTask對象只能執行一次,也就是隻能調用一次excute方法,否則會報異常。
- Android1.6之前AsyncTask是串行執行任務。Android1.6到Android3.0是並行執行任務。Android3.0以後是串行執行任務,不過我們可以通過自定義線程池,調用executeOnExecutor()方法,將自定義的線程傳入,然後並行處理。
- 不要顯示去調用onPreExecute等方法。
源碼分析
基本使用介紹完畢後,我們來看下AsyncTask的內部實現,先從我們的調用方法excute開始:
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;
}
這邊分析的源碼是android4.4,默認傳入線程池是串行去處理的。代碼3行開始,我們可以看到這裏有個狀態判斷。總共有3個狀態:Status.PENDING,Status.RUNNING,Status.FINISHED;執行前,執行中,執行完。代碼17行我們就能看到onPreExecute()方法是最先執行的。20行exec.execute(mFuture);,線程池開始工作:
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 和THREAD_POOL_EXECUTOR。從代碼中我們可以看出:SerialExecutor主要是做任務的排隊,而THREAD_POOL_EXECUTOR纔是真正的做後臺工作。
在AsyncTask中就初始化了兩個對象mWorker和mFuture,這個在executeOnExecutor方法裏就應用到。而上述代碼第9行,調用mFuture.run方法,就保證了mWorker在線程池中調用call方法:
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));
}
};
進入postResult方法:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
這裏就用到異步消息機制,進入handler:
private static class InternalHandler extends Handler {
@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;
}
}
}
我們可以看到InternalHandler 是一個靜態Handler對象,爲了能夠將環境最終切換到主線程,那麼InternalHandler就必須在主線程中。由於靜態成員會在加載類的時候初始化,那麼就變相的要求AsyncTask必須在主線程中加載了。這也印證了我們在前面提出的觀點。
然後,這裏對消息的類型進行了判斷,如果這是一條MESSAGE_POST_RESULT消息,就會去執行finish()方法,如果這是一條MESSAGE_POST_PROGRESS消息,就會去執行onProgressUpdate()方法。進入finish():
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果當前任務被取消掉了,就會調用onCancelled()方法,如果沒有被取消,則調用onPostExecute()方法,這樣當前任務的執行就全部結束了。
上述有一個消息MESSAGE_POST_PROGRESS處理,然後調用onProgressUpdate,發消息的地方,我們應該能想到是:publishProgress方法,如下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
至此,我們相信我們對於AsyncTask的工作原理及注意事項已經理解的很透徹了。