AsyncTask的使用及原理分析

Android的AsyncTask比Handler更輕量級一些,是用來做簡單的異步處理的。

使用的優點:

l  簡單,快捷

l  過程可控       

使用的缺點:

l  在使用多個異步操作和並需要進行Ui變更時,就變得複雜起來。

AsyncTask定義了三種泛型類型 Params,Progress和Result。(也是可以指定爲空的,如 AsyncTask <Void, Void, Void>)

  • Params 啓動任務執行的輸入參數。
  • Progress 後臺任務執行的百分比。
  • Result 後臺執行任務最終返回的結果。

一個異步加載數據最少要重寫以下這兩個方法:

  • doInBackground(Params…) 後臺執行,比較耗時的操作都可以放在這裏。此方法在後臺線程執行,完成任務的主要工作。在執行過程中可以調用publishProgress(Progress…)來更新任務的進度。其參數對應AsyncTask的第一個參數,publishProgress(Progress…)的參數對應AsyncTask的第二個參數,其返回值對應AsyncTask的第三個參數。
  • onPostExecute(Result)  相當於Handler 處理UI的方式,在這裏面可以使用在doInBackground 得到的返回結果操作UI。 此方法在主線程執行,任務執行的結果(doInBackground的返回值)作爲此方法的參數。

當然了,還可以重寫以下這三個方法,但不是必須的:

  • onProgressUpdate(Progress…)   可以使用進度條增加用戶體驗度。 此方法在主線程執行,用於顯示任務執行的進度。
  • onPreExecute()        調用excute()方法時執行,當任務執行之前開始調用此方法。
  • onCancelled()             用戶調用取消(調用了cancel(true))時,要做的操作。

使用AsyncTask類,以下是幾條必須遵守的準則:

1、Task的實例必須在UI thread中創建;

2、execute方法必須在UI thread中調用;

3、不要手動的調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法;

4、該task只能被執行一次,多次調用時將會出現異常;

執行的順序是:onPreExecute(),doInBackground(),onProgressUpdate(),onPostExecute()。onCancelled()不一定會執行。

除了doInBackground是在子線程執行,其他的都是在UI線程執行的。

下面說下我們具體是怎麼使用的:

第一步,創建一個異步任務的實例,並且執行,這裏傳入的參數對應的是AsyncTask的第一個參數:

MyTask mTask = new MyTask();  
mTask.execute(url);
第二步,建立一個異步任務的類,如下:

private class MyTask extends AsyncTask<String, Integer, String> {
	//onPreExecute方法用於在執行後臺任務前做一些UI操作  
	@Override  
	protected void onPreExecute() {
		textView.setText("start loading...");  
	}  
	  
	//doInBackground方法內部執行後臺任務,不可在此方法內修改UI。這裏傳入的參數對應AsyncTask的第一個參數,即execute()函數傳遞過來的
	@Override  
	protected String doInBackground(String... params) {  
		... //做一些耗時的動作
		publishProgress(progresses); //progresses對應AsyncTask的第二個參數,同時也是要傳給onProgressUpdate()的參數
		...
		return result; //返回值對應AsyncTask的第三個參數,同時也是要傳給onPostExecute()的參數
	}  
	  
	//onProgressUpdate方法用於更新進度信息  
	@Override  
	protected void onProgressUpdate(Integer... progresses) {
		textView.setText("loading..." + progresses[0] + "%");  
	}  
	  
	//onPostExecute方法用於在執行完後臺任務後更新UI,顯示結果  
	@Override  
	protected void onPostExecute(String result) {
		textView.setText(result);  
	}  
	  
	//onCancelled方法用於在取消執行中的任務時更改UI  
	@Override  
	protected void onCancelled() {
		textView.setText("cancelled");
	}  
}
第三步,不是必須的,如果需要取消任務,可以調用:
mTask.cancle(true);
該函數一旦被調用就不會執行到onPostExecute()了,而是執行onCancelled()方法。

這麼簡單強大的功能是怎麼實現的呢?我們跟着源碼來一探究竟。
先看下AsyncTask的構建函數:
public AsyncTask() {  
	mWorker = new WorkerRunnable<Params, Result>() {  
		public Result call() throws Exception {  
			mTaskInvoked.set(true);  
			Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
			return postResult(doInBackground(mParams));  
		}  
	};  
	mFuture = new FutureTask<Result>(mWorker) {  
		@Override  
		protected void done() {  
			...
		}  
	};  
}
就是建立了WorkerRunnable和FutureTask兩個實例,並把mWorker傳遞給了mFuture。
我們要啓動某一任務都會執行下execute()方法,看下它的源碼,如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
    return executeOnExecutor(sDefaultExecutor, params);  
}
調用了executeOnExecutor,繼續看下實現:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  
        Params... params) {  
    ...
    mStatus = Status.RUNNING;  
    onPreExecute();  
    mWorker.mParams = params;  
    exec.execute(mFuture);  
    return this;  
}
在這裏我們看到了onPreExecute(),因此可以看出onPreExecute()是首先被執行的,然後調用了exec.execute(),從上面的代碼中我們看到exec就是sDefaultExecutor,那麼sDefaultExecutor是什麼東東呢?我們看到了如下代碼:
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();  
……  
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
也就是我們實際調用的是SerialExecutor的execute()方法,看下實現:
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);  
        }  
    }  
}
首先把任務放到mTasks這個集合裏面;然後判斷mActivie如果爲空,就調用scheduleNext ()方法。
mActivie爲null的意思是當前沒有任務在執行,如果mActivie!=null,那麼說明當前有任務正在執行,那麼只要把任務添加到mTasks裏面即可。
因爲任務執行完畢後,會再次調用scheduleNext()方法的,就是
finally {
scheduleNext();
}
這樣就形成了一種鏈狀調用結構,只要mTasks裏面還有任務,就會不斷逐一調用,如果後面有任務進來,就只要添加到mTasks裏面即可。
這裏給execute()傳遞的參數是mFuture,所以會執行到mFuture的run()方法,而run()方法最終會調用如下方法:
void innerRun() {  
    if (!compareAndSetState(READY, RUNNING))  
        return;  
    runner = Thread.currentThread();  
    if (getState() == RUNNING) { // recheck after setting thread  
        V result;  
        try {  
            result = callable.call();  
        } catch (Throwable ex) {  
            setException(ex);  
            return;  
        }  
        set(result);  
    } else {  
        releaseShared(0); // cancel  
    }  
}
我們看到有調用callable.call(),而callable就是mWorker了,所以這裏就執行到了下面的函數:
public Result call() throws Exception {  
	mTaskInvoked.set(true);  
	Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
	return postResult(doInBackground(mParams));  
}
postResult(doInBackground(mParams))這一句就走到了doInBackground()方法了,並且將它的返回值傳給了postResult,那我們看下postResult的具體實現吧!
private Result postResult(Result result) {  
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
            new AsyncTaskResult<Result>(this, result));  
    message.sendToTarget();  
    return result;  
}
原來是使用了sHandler來發送消息,那我們再看下處理消息的地方:
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:                   
                result.mTask.finish(result.mData[0]);  
                break;  
            case MESSAGE_POST_PROGRESS:  
                result.mTask.onProgressUpdate(result.mData);  
                break;  
        }  
    }  
}
收到MESSAGE_POST_RESULT就執行finish(),收到MESSAGE_POST_PROGRESS就執行onProgressUpdate()。我們看下finish()的實現:
private void finish(Result result) {  
    if (isCancelled()) {  
        onCancelled(result);  
    } else {  
        onPostExecute(result);  
    }  
    mStatus = Status.FINISHED;  
}
這裏做了一個判斷,根據任務是否被取消來調用不同的方法。
那MESSAGE_POST_PROGRESS消息是什麼時候發送的呢?猜想一下,肯定是在publishProgress()裏面,看下實現:
protected final void publishProgress(Progress... values) {  
    if (!isCancelled()) {  
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,  
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();  
    }  
}
至此AsyncTask相關的流程就講的差不多了,可以看到AsyncTask的任務執行是單線程的,關於AsyncTask的任務執行是單線程實現還是多線程實現是經歷了一番變化的,較早的版本是單線程實現,從Android2.X開始,Google把它改爲多線程實現,後來發現,多線程實現的話,會有很多需要保證線程安全的額外工作留給開發者,所以從Android3.0開始,又把默認實現改爲單線程了。
使用多線程實現的話,有一個很大的缺陷,那就是線程池總大小是128,也就是如果任務數量超過了128,那麼程序就會崩潰,如果有興趣可以看我下面一篇文章《Android AsyncTask兩種線程池分析和總結 》
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章