1,前段時間換工做的時候,關於AsyncTask源碼這個點基本上大一點的公司都會問,因此今天就和你們一塊兒來總結總結。原本早就想寫這篇文章的,當時寫《Android -- 從源碼解析Handle+Looper+MessageQueue機制》的時候就是想爲這篇文章作鋪墊的,由於AsyncTask說裏面仍是使用的handle,因此先就寫了handle這一篇。記得15年末去美團面試的時候,面試官就問我既然存在handle爲何google還要出AsyncTask(畢竟底層仍是用的handle+Executor),當時也是不太懂。只是簡單的說了下AsyncTask是對handle的封裝,可能更加優化,性能好之類的。因此今天就帶你們一塊兒看一下它底層究竟是怎麼實現的。html
2,分析+實例java
在12年以前那時候尚未一些xtuils、volley的第三方的網絡框架,也沒有asyncTask的出現,那時候要請求一個網絡數據,首先build request參數,而後因爲請求網絡是耗時操做,因此你得有個Executer或者線程,而後enqueue後,經過線程去run你的請求,獲得服務器數據後,callback回調給你的上層。android
在沒有框架的年代,想要作一次請求,是萬分痛苦的,你須要本身管理線程切換,須要本身解析讀取數據,解析數據成對象,切換回主線程,回調給上層。git
而後咱們的AsyncTask順勢而生了,它不須要咱們程序員再手動管理線程,動手寫回調之類,爲了防止有些同窗壓根都不知道這個類,因此這裏我仍是帶着你們從一些概念到實例,再到源碼。程序員
public abstract class AsyncTask<Params, Progress, Result> { }
當咱們使用asynctask的時候通常都會建立一個類繼承自它,且須要肯定它的三個泛型,這裏的三種泛型類型分別表明「啓動任務執行的輸入參數」、「後臺任務執行的進度」、「後臺計算結果的類型」。在特定場合下,並非全部類型都被使用,若是沒有被使用,能夠用Java.lang.Void類型代替。github
在通常的get請求狀況下咱們第一個參數是傳遞的咱們請求的地址,因此這裏咱們會傳遞一個String,而第二個參數是咱們的後臺執行的進度,若是你的需求須要實時的向用戶展現請求的進度的話這裏就能夠填寫Integer類型,否寫能夠寫上Void,第三個參數是咱們請求的結果,通常是byte[],String等,能夠根據具體的需求。面試
再讓咱們看看它裏面的幾個重要的方法:服務器
① onPreExecute():通常用來在執行後臺任務前對UI作一些標記,例如dialog的show。 ② doInBackground(Params... params) :用於執行較爲耗時的操做,此方法將接收輸入參數和返回計算結果。在執行過程當中能夠調用publishProgress(Progress... values)來更新進度信息。 ③ publishProgress(Progress... values):用來更新進度 ④ onProgressUpdate(Progress... values):在調用publishProgress(Progress... values)時,此方法被執行,直接將進度信息更新到UI組件上。 ⑤.onPostExecute(Result result):當後臺操做結束時,此方法將會被調用,計算結果將作爲參數傳遞到此方法中,將結果回調顯示到UI組件上。
這裏須要注意幾個點:上面這五個方法都不是手動調用的,當你調用ascyntask.execute()方法以後上面的方法會自動調用、doInBackground是運行在子線程,不能進行ui操做。好了,大體的知識點都瞭解了,咱們開始寫一個栗子。網絡
先看一下效果:併發
讓咱們直接來看一下自定義的MyAsyncTask的代碼:
MyAsyncTask.java
public class MyAsyncTask extends AsyncTask<String ,Integer,byte[]> { /** * 被調用後當即執行,通常用來在執行後臺任務前對UI作一些標記 */ @Override protected void onPreExecute() { super.onPreExecute(); Log.i("wangjitao","onPreExecute"+Thread.currentThread().getName()); } /** * 在onPreExecute()完成後當即執行,用於執行較爲費時的操做,此方法將接收輸入參數和返回計算結果。在執行過程當中能夠調用publishProgress(Progress... values)來更新進度信息。 * @param params * @return */ @Override protected byte[] doInBackground(String... params) { Log.i("wangjitao","doInBackground"+Thread.currentThread().getName()); //請求網絡數據 InputStream inputStream = null; HttpURLConnection httpURLConnection = null; try { URL url = new URL(params[0]); if (url != null) { httpURLConnection = (HttpURLConnection) url.openConnection(); // 設置鏈接網絡的超時時間 httpURLConnection.setRequestMethod("GET");//GET和POST必須全大寫 httpURLConnection.setConnectTimeout(10000);//鏈接的超時時間 httpURLConnection.setReadTimeout(5000);//讀數據的超時時間 // 表示設置本次http請求使用GET方式請求 httpURLConnection.setRequestMethod("GET"); int responseCode = httpURLConnection.getResponseCode(); if (responseCode == 200) { // 從服務器得到一個輸入流 InputStream is = httpURLConnection.getInputStream(); long total = httpURLConnection.getContentLength(); int count = 0; ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len=0; while((len=is.read(buffer))!=-1){ baos.write(buffer, 0, len); count += len; publishProgress((int) ((count / (float) total) * 100)); //爲了演示進度,休眠500毫秒 Thread.sleep(500); } is.close(); byte[] datas = baos.toByteArray(); baos.close(); return datas; } } } catch (Exception e) { } return null; } /** * 當前進度的更新 * @param values */ @Override protected void onProgressUpdate(Integer... values) { Log.i("wangjitao","onProgressUpdate"+Thread.currentThread().getName()); mDialog.setProgress(values[0]); } /** * 當後臺操做結束時,此方法將會被調用,計算結果將作爲參數傳遞到此方法中,直接將結果顯示到UI組件上。 * @param bytes */ @Override protected void onPostExecute(byte[] bytes) { Log.i("wangjitao","onPostExecute"+Thread.currentThread().getName()); mDialog.dismiss(); mImageView.setImageBitmap(BitmapFactory.decodeByteArray(bytes, 0, bytes.length)); } }
這裏在點擊button以後我調用了new MyAsyncTask().execute(url);,而後再MyAsyncTask的每一個方法裏面添加上了當前線程的log,想看一下doInBackground是否是像咱們網上說的運行在子線程,這裏注意一下,咱們請求的是網絡數據,記得在清單文件加上網絡權限,打印結果以下:
08-16 05:36:24.978 19453-19453/com.ysten.anysctasksource I/wangjitao: onPreExecute:main 08-16 05:36:24.979 19453-23587/com.ysten.anysctasksource I/wangjitao: doInBackground:AsyncTask #1 08-16 05:36:28.573 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:29.074 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:29.574 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:30.075 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:30.575 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:31.086 19453-19453/com.ysten.anysctasksource I/wangjitao: onProgressUpdate:main 08-16 05:36:31.588 19453-19453/com.ysten.anysctasksource I/wangjitao: onPostExecute:main
能夠看到doInBackground的確運行在子線程中,過一下咱們從源碼裏面來驗證一下。
3,源碼分析
咱們知道啓動咱們整個任務的就是咱們new MyAsyncTask().execute(url);方法,因此咱們首先來看一下execute()方法
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } @MainThread 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; }
第3行:咱們知道execute方法是調用本類的executeOnExecutor。
第10-20行:用來判斷當前AsyncTask的狀態,一共存在三種狀態 :PENDING(就緒)、RUNNING(運行)、FINDISH(完成),保證每次都是第一次運行此異步任務,否則就拋異常
第22行:將當前的運行狀態切換至RUNNING狀態
第24行:執行了咱們的onPreExecute() ,注意一下,這時候咱們的第一個準備操做的方法被執行了(因此通常咱們在這個方法作一些ui的準備操做)
第26行:將咱們的params複製成員變量mWorker對象的mParams上(有同窗說這裏我還不知道mWorker,先不要慌,繼續往下看)
第27行:exec.execute(mFuture);這行代碼咱們更加一臉懵逼,mFuture、exec分別表明什麼對象呢?exec.execute()是用來幹什麼的呢?
ok,看完 上述代碼咱們必定對mWorker、mFuture、exec這些成員變量有所疑惑吧,沒事咱們來一點點的來看。
首先來看一下咱們的mWorker到底是什麼,源碼以下:
private final WorkerRunnable<Params, Result> mWorker; private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; } public interface Callable<V> { V call() throws Exception; } mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } };
第4-10行:能夠看到咱們的mWorker是Callable的子類,包含一個call方法和一個變量mParams。
這裏要補充一下,mWorker是在構造函數中初始化的,由於是一個抽象類,在這裏new了一個實現類,實現了call方法
第14-27行:首先將mTaskInvoked的value設置成true(這裏要留心一下這個操做,後面還有地方對mTaskInvoked進行判斷的),而後Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);設置子線程優先執行,而後就執行到了doInBackground(),看到沒,這裏就到了咱們的第二個方法,這裏還有一個疑問,咱們上面知道doInBackground是執行在子線程中的,那怎麼執行在子線程中呢(咱們帶着這個問題),先繼續往下看。
當運行到finally代碼塊中調用的是 postResult(result);咱們接下來看看postResult()的具體代碼:
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } @SuppressWarnings({"RawUseOfParameterizedType"}) private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
第3-5行:很熟悉沒?瞭解咱們的異步消息handle的,都知道這是幹什麼的而 AsyncTaskResult類就是把當前從網絡下請求的結果數據result保存到data中
看到這,我相信你們確定會想到,在某處確定存在一個handler,且重寫了handleMessage方法等待消息的傳入
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; } } }
果真,在handleMessage中咱們的MESSAGE_POST_RESULT以後時調用的自己的finish()方法(這裏也請你們留心一下MESSAGE_POST_PROGRESS這個消息,後面也會用到),那麼讓咱們再看看finish裏面的具體代碼吧
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
首先判斷當前的asynctask是否被取消,若果沒取消則執行onPostExecute(result),這時候數據已經回調了,到這裏咱們就差很少執行完了幾個重要的方法了,而後再將asynctask的執行狀態切換到FINISH狀態。
到這裏咱們的mWorker的工做流程所有了解了,繼續往下看到咱們mFuture這個變量
private final FutureTask<Result> mFuture; 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); } } };
mFuture這個變量也是初始化在咱們的構造函數裏面,首先咱們不去看FutureTask這個類中的具體代碼包含的方法和變量(因爲代碼有點多,後面的話單獨的給你們分析一下),咱們先繼續往下看,mFuture的初始化是將將mWorker做爲參數,並重寫其done()方法。
done方法中具體的時調用postResultIfNotInvoked()方法,而get()方法裏面是獲取的咱們的Result泛型,拿到的是咱們mWorker.call的返回值,看一下具體的代碼
private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } }
若是mTaskInvoked不爲true,則執行postResult;可是在mWorker初始化時就已經將mTaskInvoked爲true(我在上面call方法裏面提醒過你們的),因此通常這個postResult執行不到。
ok,到這裏咱們咱們基本上都瞭解都mWorker、mFuture的意思了,接下來看看咱們的exec這個變量的含義吧
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; public static final Executor SERIAL_EXECUTOR = new 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); } } }
第1-3行:從上面的代碼咱們能夠知道,exec爲executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor
第5-7行:sDefaultExecutor其實是一個SerialExecutor對象
第9-26行:這裏咱們能夠看到SerialExecutor裏面內部維持一個任務隊列,在execute方法中將runnable放入mTasks隊尾,而後判斷當前mActive是否爲空,若是不爲空在調用scheduleNext()方法;
第28-33行:取出任務隊列中的隊首任務,若是不爲null則賦值給mActive對象並傳入THREAD_POOL_EXECUTOR進行執行。
因此到這裏咱們再來看看THREAD_POOL_EXECUTOR是一個什麼鬼!
public static final Executor 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; private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30;
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);
能夠看到THREAD_POOL_EXECUTOR其實就是一個線程池
第9-12行:是一些線程池的配置,在3.0之前最大線程併發數MAXIMUM_POOL_SIZE是一個固定值128,如今都是根據當前cpu合數來動態設置的,例如如今的牀鋪是四核的,因此MAXIMUM_POOL_SIZE爲9,而且將阻塞線程由之前的10個變成如今的128個(記得前不久面試的時候面試官就問道爲何會設置128這個數值爲何不是129或者140,到如今我仍是不知道怎麼回答這個問題),那麼是否是意味着咱們併發的線程數超過137個以後,咱們的程序就會拋異常,其實不是這樣的,咱們以前的SerialExecutor 類中的execute裏面的代碼
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); } }
當存在10個任務的時候,第一個任務進來,mActive爲空執行scheduleNext,在scheduleNext方法中取出線程頭部添加到線程池中,而後複製給mActive,當第二個任務進來時,mActive不爲空,也就是說不執行scheduleNext()方法,因此只有等到第一個任務run方法執行完以後調用finally中的scheduleNext()纔會執行下一個任務,因此來講其實仍是單線程線性執行,一個接一個。
ok,到這裏咱們基本上完成了對源碼的解讀,可是咱們仍是有幾個疑問,咱們的onProgressUpdate()和publishProgress()怎麼沒有執行啊,onProgressUpdate方法只有在調用publishProgress()以後才執行,因此讓咱們來看看publishProgress()的源碼
@WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
很熟悉,又發現了咱們的handle,繼續找到咱們的消息接受的地方
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; } } }
這不是咱們剛剛看過的代碼,沒錯,上面我叫你們留意過的MESSAGE_POST_PROGRESS就是這個用於更新咱們進度的,在裏面直接調用onProgressUpdate()方法。
ok,咱們如今仍是有一個疑問,就是爲何說doInBackground運行在子線程中
咱們進過上面的源碼分析指導doInBackground是在mWorker的call方法中調用的,因此咱們如今只須要在哪裏調用了mWorker.call()代碼就行
mWorker = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); Result result = null; try { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked result = doInBackground(mParams); Binder.flushPendingCommands(); } catch (Throwable tr) { mCancelled.set(true); throw tr; } finally { postResult(result); } return result; } };
因爲咱們知道mWorker是以參數傳遞到mFuture中的,因此咱們仍是要看FutureTask這個類的源碼
public class FutureTask<V> implements RunnableFuture<V> { public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } 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 must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } } }
這裏我只展現除了重要的幾個方法,咱們知道mFuture是以參數傳遞到SerialExecutor類中的execute方法中
public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } }
因此這裏new了一個runnable建立了一個子線程,並調用mFuture的run方法,而在mFuture的run方法中有一行result = c.call();這裏的c就是咱們的mWorker,因此這也就解釋了咱們的doInBackground是運行在子線程中。
ok,到這裏咱們的問題解決的差很少了,可是有一些公司喜歡問一下3.0以前的asynctask 的缺陷問題,在這裏給你們找了一寫,就直接貼出來了,夠你們應付面試,具體是怎麼產生的就你們本身去研究了,篇幅有點長,就不過多介紹了。
若是如今你們去面試,被問到AsyncTask的缺陷,能夠分爲兩個部分說,在3.0之前,最大支持128個線程的併發,10個任務的等待。在3.0之後,不管有多少任務,都會在其內部單線程執行;
這是GitHub代碼,有須要的同窗能夠去下載一下.在寫線程池這一塊感受本身不是很熟悉,因此下一篇打算來總結總結線程池。