Android 源码解析之AsyncTask
AsyncTask相信大家都不陌生,它是為了簡化異步請求、更新UI操作而誕生的。使用它不僅可以完成我們的網絡耗時操作,而且還可以在完成耗時操作后直接的更新我們所需要的UI組件。這使得它在android開發中成為炙手可熱的網絡請求工具類。
而今天我們就以源碼分析的形式來徹底的學習下它的實現過程。
首先,我們先看看AsyncTask的定義形式:
public abstract class AsyncTask<Params, Progress, Result> { }首先AsyncTask它是一個抽象類,包括三種泛型類型,具體含義如下:
- Params:它表示請求參數的類型
- Progress:執行任務的進度類型
- Result:返回結果的類型
但是以上三個參數并不是一定必須,在不需要時可以設置為Void,沒有返回類型。
然后我們看看它的執行過程,包括以下幾個方法:
execute(Params... params),我們在執行異步操作時會調用該方法,表示開始執行任務。
protected void onPreExecute() {},在調用execute方法后,該方法就會得到執行,它執行在UI線程中,用來初始化一些UI空間等
protected abstract Result doInBackground(Params... params);在onPreExecute執行完后將會執行該方法,它執行在后臺,并接受一個Params類型的數組參數,用于請求網絡,并且它返回一個Result 類型的結果。該方法中可以在執行網絡請求的同時更新請求進度,調用publishProgress(Progress... values) 。
protected void onProgressUpdate(Progress... values) ,假如在doInBackground方法中調用了publishProgress方法,那么該方法就會得到執行,它是執行在UI線程的,根據values的值不停的更改進度,以達到想要的效果。
protected void onPostExecute(Result result),該方法是在doInBackground方法執行完畢后得到執行,可根據doInBackground返回的結果進行后續的UI操作,由此可見它是工作在UI線程中的。
經過上面一系列的方法運轉,一個完整的AysncTask請求就正式的完成了任務。不僅完成了耗時操作還更新的UI組件,這就是它的魅力所在。但是這時候你該有疑問了,上面的方法都是你說執行哪個就執行哪個,哪到底是怎么執行的呢?
那么接下來就正式的揭開它的廬山正面目。
在正式介紹它的源碼之前,你必須知道new 一個類它所執行的過程:
在new的過程中,它會首先一層一層的加載它所繼承的父類的成員變量及構造方法
然后在加載自己的成員變量和構造方法。
順序是不可變得。
那么看看在我們執行 new AsyncTask()中,它到底加載了哪些成員呢?
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();private static final int CORE_POOL_SIZE = CPU_COUNT + 1;private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;private static final int KEEP_ALIVE = 1;private static final ThreadFactory sThreadFactory = new ThreadFactory() {private final AtomicInteger mCount = new AtomicInteger(1);public Thread newThread(Runnable r) {return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());}};private static final BlockingQueue<Runnable> sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128);public static final Executor THREAD_POOL_EXECUTOR= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);public static final Executor SERIAL_EXECUTOR = new SerialExecutor();private static final int MESSAGE_POST_RESULT = 0x1;private static final int MESSAGE_POST_PROGRESS = 0x2;private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;private static InternalHandler sHandler;private final WorkerRunnable<Params, Result> mWorker;private final FutureTask<Result> mFuture;private volatile Status mStatus = Status.PENDING;private final AtomicBoolean mCancelled = new AtomicBoolean();private final AtomicBoolean mTaskInvoked = new AtomicBoolean();private static class SerialExecutor implements Executor{...}public enum Status {PENDING,RUNNING,FINISHED,}看到這么一大堆是不是很麻頭皮,其實仔細拆分下來,你主要看幾個變量即可。
THREAD_POOL_EXECUTOR :這個成員變量從它THREAD_POOL_EXECUTOR = new ThreadPoolExecutor中可以看出,它是一個線程池,而ThreadPoolExecutor線程池中需要幾個參數,如corePoolSize(核心線程數)、maximumPoolSize(最大線程數)、workQueue(任務隊列)、threadFactory(線程工程)等等,所以像CORE_POOL_SIZE,sPoolWorkQueue ,sThreadFactory 等成員變量,只是為了配置這個線程池而已。
sDefaultExecutor 這個成員變量是默認的線程調度任務,從上面可看出SERIAL_EXECUTOR則是一個序列化的任務調度,從sDefaultExecutor = SERIAL_EXECUTOR;中可以明確的知道sDefaultExecutor任務調度中是按先后順序執行的。
sHandler顧名思義是一個handler,mWorker是一個工作線程,mFuture則是一個FutureTask,FutureTask是專門用于管理Runnable線程的,mStatus 則是一個枚舉,里面有三種狀態,分別是未執行,執行中,以及執行完成狀態,默認狀態是未執行狀態。
所以我們只要理解好上面幾個變量可以不用害怕它一堆的初始化成員。
然后我們在看看AysncTask的構造方法中具體做了那些事:
public AsyncTask() {mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedResult result = doInBackground(mParams);Binder.flushPendingCommands();return postResult(result);}};mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected 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);}}};}簡單來說,AsyncTask的成員變量中就只是初始化了兩個變量,mWorker 和 mFuture 。這兩個變量是非常重要的,后續的所有執行過程都是由這兩個變量構成或引導的。
首先mWorker 是一個抽象內部類實例,是一個任務線程,它實現Callable接口中的call()方法
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {Params[] mParams;}然后mFuture 則是一個針對任務線程的管理類。專門用于管理任務線程的,可以使我們的任務得到更好的控制,來看看它的構造方法吧:
public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; // ensure visibility of callable}就是接受了我們的mWorker 對象以及把自身的狀態設置為NEW。
以上就是在new一個AsyncTask所進行的所有操作,無非就是初始化了一些數據和變量。
下面來看看AysncTask的正式執行。
我們所知道開啟一個AsyncTask任務所調用的方法是execute方法,該方法必須在主線程中調用。
public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);}在調用execute方法后,該方法什么也沒做,只是把已初始化好的默認序列任務線程sDefaultExecutor和傳遞進來的數據params傳遞給了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;}executeOnExecutor()方法中首先判斷了AsyncTask的執行狀態,如果是正在執行或是已經結束執行了,它就會報出一個IllegalStateException的異常,告訴你線程或是在執行中或是已經執行完畢了。
只有在未執行的狀態下,才可以進行AsyncTask請求任務,接下來它直接把AsyncTask的執行狀態更改為Status.RUNNING,告訴其他任務該AsyncTask正在執行中,保持執行結果的一致性。然后就執行了onPreExecute();由于execute方法是必須在主線程中執行的,所以到目前為止還是在主線程中運行,也就證明了onPreExecute()方法是在主線程中運行的。
onPreExecute源碼中并沒有做什么事情,這對于我們來說,只需要重寫該方法就可以在主線程中進行一些UI組件的初始化等操作。
- 接下來則是將我們所傳遞的數據賦值給mWorker的mParams變量,然后調用exec.execute(mFuture)方法,我們通過execute方法中知道exec其實就是一個sDefaultExecutor,sDefaultExecutor實則是一個SerialExecutor 序列線程,而mFuture我們在構造方法中也很清楚的知道,它是一個封裝了mWorker線程的一個可管理的任務線程,那么在調用sDefaultExecutor的execute方法并傳遞進了mFuture任務線程,那到底做了什么事情呢,我們來看下它的源碼:
源碼我們很清晰的知道在execute方法中最終的目的就是把mFuture任務線程賦值給一個Runnable 線程并放到了THREAD_POOL_EXECUTOR線程池中,由THREAD_POOL_EXECUTOR線程池來執行mFuture線程任務。
那么接著我們看看在THREAD_POOL_EXECUTOR線程池中execute的方法中主要做了什么事情:
public void execute(Runnable command) {if (command == null)throw new NullPointerException();int c = ctl.get();if (workerCountOf(c) < corePoolSize) {if (addWorker(command, true))return;c = ctl.get();}if (isRunning(c) && workQueue.offer(command)) {int recheck = ctl.get();if (! isRunning(recheck) && remove(command))reject(command);else if (workerCountOf(recheck) == 0)addWorker(null, false);}else if (!addWorker(command, false))reject(command);}THREAD_POOL_EXECUTOR線程池中主要是判斷了傳遞的線程是否為空,是否小于當前線程池中保存的核心線程數,如果小于則直接執行addWorker(command, true)方法,下面看看addWorker方法中的實現內容:
private boolean addWorker(Runnable firstTask, boolean core) {...(前面代碼省略)boolean workerStarted = false;boolean workerAdded = false;Worker w = null;try {w = new Worker(firstTask);final Thread t = w.thread;if (t != null) {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {int rs = runStateOf(ctl.get());if (rs < SHUTDOWN ||(rs == SHUTDOWN && firstTask == null)) {if (t.isAlive()) throw new IllegalThreadStateException();workers.add(w);int s = workers.size();if (s > largestPoolSize)largestPoolSize = s;workerAdded = true;}} finally {mainLock.unlock();}if (workerAdded) {t.start();workerStarted = true;}}} finally {if (! workerStarted)addWorkerFailed(w);}return workerStarted;}我們只看主要的邏輯,首先是將我們的mFuture任務線程存放到了一個Worker的對象中,然后又從Worker對象中獲取到mFuture線程并賦值給了Thread ,接著把Worker對象放到workers的HashSet數據集合對象中,經過獲取HashSet的大小并進行一些判斷,把workerAdded 設置為true,最后開啟t.start();線程,由此進入了子線程中。
那么接下來在開啟的子線程中又做了什么事情呢?
我們從上面的分析指導t.start()開啟就是一個mFuture的異步任務線程,那么它在哪執行呢?
細心的朋友可以發現,原來是在SerialExecutor 中的execute方法中我們的mFuture的run()早已在等待了線程的啟動,那么,我現在去看看mFuture的run()方法中做了什么工作吧
public void run() {if (state != NEW ||!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))return;try {Callable<V> c = callable;if (c != null && state == NEW) {V result;boolean ran;try {result = c.call();ran = true;} catch (Throwable ex) {result = null;ran = false;setException(ex);}if (ran)set(result);}} finally {runner = null;int s = state;if (s >= INTERRUPTING)handlePossibleCancellationInterrupt(s);}}這段代碼很簡單,一眼就可以看得出來就是利用我們在為mFuture初始化時傳遞的mWorker 對象實例并調用它的call()方法,我們先不管call怎么實現的,先來看看這個方法中的后續是什么。
接著它得到一個執行結果,并把一個boolean類型的ran設置為true,最后根據ran調用set(result);方法,并把結果傳遞進去,下面看看set的源碼:
protected void set(V v) {if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {outcome = v;U.putOrderedInt(this, STATE, NORMAL); // final statefinishCompletion();}}它主要調用了finishCompletion();在來看看finishCompletion的源碼:
private void finishCompletion() {// assert state > COMPLETING;for (WaitNode q; (q = waiters) != null;) {if (U.compareAndSwapObject(this, WAITERS, q, null)) {for (;;) {Thread t = q.thread;if (t != null) {q.thread = null;LockSupport.unpark(t);}WaitNode next = q.next;if (next == null)break;q.next = null; // unlink to help gcq = next;}break;}}done();callable = null; // to reduce footprint}在執行完call中,把一些對象進行還原,還調用了 done(),該方法就是在AsyncTask構造方法中我們有看到它的實現:
mFuture = new FutureTask<Result>(mWorker) {@Overrideprotected 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);}}};我們也說過FutureTask主要是用來管理異步線程任務的,那么在done方法中就有很好的體現,在該方法中,它會判斷執行的結果是否成功,成功后有沒有被發送,如果有發送它就不再發送消息,如果結果執行成功,但沒有被發送它就會發送最終的執行結果:
private void postResultIfNotInvoked(Result result) {final boolean wasTaskInvoked = mTaskInvoked.get();if (!wasTaskInvoked) {postResult(result);}}postResult方法的內容我們推后一點講,那么現在我們來看看mWorker 中call()是怎么實現的:
mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {mTaskInvoked.set(true);Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);//noinspection uncheckedResult result = doInBackground(mParams);Binder.flushPendingCommands();return postResult(result);}};在這里我們終于見到了我們所熟悉的一個方法doInBackground(),由此也可以知道其確實是在子線程運行的,而doInBackground()方法在AsyncTask類中是一個抽象方法:
protected abstract Result doInBackground(Params... params);那么我們在重寫doInBackground()時就可以直接的在其中進行一些耗時的網絡和IO操作了。
這里插上一句,假如在doInBackground()調用了publishProgress方法來更新進度的話,那來看看它是怎么做的:
protected final void publishProgress(Progress... values) {if (!isCancelled()) {getHandler().obtainMessage(MESSAGE_POST_PROGRESS,new AsyncTaskResult<Progress>(this, values)).sendToTarget();}}publishProgress方法中主要是通過Hangler發送一條更新進度的標志用來更新進度。這里的Hangler接受消息在下面和執行結果一起講。
最后doInBackground()執行獲取的Result 結果也將會傳遞到postResult(result);方法中,那么現在我們來看看它的源碼實現:
private Result postResult(Result result) {@SuppressWarnings("unchecked")Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));message.sendToTarget();return result;}private static Handler getHandler() {synchronized (AsyncTask.class) {if (sHandler == null) {sHandler = new InternalHandler();}return sHandler;}}private static class AsyncTaskResult<Data> {final AsyncTask mTask;final Data[] mData;AsyncTaskResult(AsyncTask task, Data... data) {mTask = task;mData = data;}}postResult中首先封裝了doInBackground異步執行結果的AsyncTaskResult對象,然后獲取到一個Handler ,通過消息處理機制發送一條信息來切換到主線程中進行UI界面的更換,消息處理機制不屬于本次博文的內容所以不再細說,那來看看這個Handler是怎么處理這個消息內容的。
private static class InternalHandler extends Handler {public InternalHandler() {super(Looper.getMainLooper());}@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})@Overridepublic 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;}}}Handler中主要是根據消息標志進行區分是更新進度還是執行結果:
如果是更新進度則調用AsyncTask的onProgressUpdate方法來更新內容,由于通過Handler已轉變為主線程中,所以我們在重寫該方法時可以直接更新UI組件。
如果是執行結果則AsyncTask的finish(result.mData[0]);并把結果數據傳遞過去,來看看finish()中是怎么實現的:
private void finish(Result result) {if (isCancelled()) {onCancelled(result);} else {onPostExecute(result);}mStatus = Status.FINISHED;}finish()方法中也非常的簡單,首先判斷是否為取消線程,否則的話則執行onPostExecute(result)方法,由此我們在重寫了onPostExecute方法后可以直接的更新我們的UI組件。
最后把AsyncTask的狀態改為完成狀態,至此整個AsyncTask生命周期就執行完畢了。
好了,至此AsyncTask整個執行過程就完全講完了,相信大家也學到了不少東西,建議大家有空自己對著源碼在梳理一遍,畢竟自己總結出來的印象就更深刻。
今天就到這里吧,祝大家生活愉快。
更多資訊請關注微信平臺,有博客更新會及時通知。愛學習愛技術。
轉載于:https://www.cnblogs.com/guanmanman/p/6076418.html
總結
以上是生活随笔為你收集整理的Android 源码解析之AsyncTask的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 利用 Chef 在 Red Hat En
- 下一篇: [转]九个Console命令,让js调试