由于從wordpress將文章倒回,發現格式有點混亂,經努力調整后依然有部分的格式還未調教好,請多多包涵.
分析AsyncTask或者闡述AsyncTask的博文有很多,本著授人予魚不如授人予漁的想法我想通過自主學習的方式去探索這個api,如果有闡述不當的地方歡迎各位大神不吝斧正.本文將通過源碼學習分析以及demo實例驗證的方式徹底的了解AsyncTask運行的原理.
隨著開源社區的興旺,在目前android的開發中有著各種異步的工具以及框架,現在比較熱門的當屬Rxjava+RxAndroid這個React的開源庫,至于這個從Android1.5版本就開始提供的異步工具AsyncTask反而經常出現在Android各類異步實現文章中的反面例子.我寫這篇文章有以下的意圖:一是通過源碼和實例去探討為什么AsyncTask一直被吐槽.二是了解學習AsyncTask的實現方式(只有了解原理,在使
用中遇到各式問題才有對策).
關于AsyncTask的介紹以及使用例子可以移步官方開發者網站,這里就不多費口舌.AsyncTask的存在的原因是在Android應用運行過程中,耗時的任務不能放在UI線程也就是我們常說的主線程中執行,必須開啟線程在后臺執行,如果有執行結果要通知到頁面上的話需要通過handler進行子線程和UI線程的通信,而AsyncTask就是應運而生去簡化這一過程的api.
現在進入實現原理的正題,通過學習源碼的過程可以設想如果是要求自己實現一個這樣的異步工具應當怎么做,任何創造的前提是從模仿開始.筆者這邊使用的sdk version 23的源碼.首先是類的定義:
public abstract class AsyncTask {
? ? private static final String LOG_TAG = "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 sPoolWorkQueue =new LinkedBlockingQueue(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
AsyncTask是抽象類,根據使用的需要定義了三個泛型分別是傳入的參數,執行的Progress,以及執行的結果.通過這一小段的源碼可以看到AsyncTask擁有一個靜態final的THREAD_POOL_EXECUTOR,這個線程池能夠運行的線程最大數是通過 cpu核心數*2+1 計算得出.并且在一個進程中無論開發者寫出多少個AsyncTask的實現子類最終管理線程的只是這個大小為128的線程池.如果連續添加超過128個任務,線程池就爆了(這也是被其他開發者詬病的地方,不過這樣的開發寫法是否符合規范也待商榷).現在接著往下看:
/**
* An {@link Executor} that executes tasks one at a time in serial
* order.? This serialization is global to a particular process.
*/
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 mWorker;
private final FutureTask 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 {
final ArrayDeque mTasks = new ArrayDeque();
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);
}}}
剛才執行的線程池構建好了,現在輪到Exector SERIAL_EXECUTOR,看這個對象的命名為serial,感覺像是單個按順序的執行器(非并發),我們接著往下看:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
默認的executor為SERIAL_EXECUTOR 并帶上了volatile(這里帶上volatile類型修飾符的原因是在多線程編程中開發者可以通過調用AsyncTask.setDefaultExecutor(Executor executor)使用自定義的Executor替換SERIAL_EXECUTOR.此處先不急著看SERIAL_EXECUTOR.接著是
private static InternalHandler sHandler;
又是一個靜態的對象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;
}
}
}
根據對象名稱有internal說明是一個內部的事件handler,構造函數帶有super(Looper.getMainLooper())說明是這個Handler的handleMessage(Message msg)的方法是在主線程中調用,分別有兩個事件是處理result信息和progress信息這里也就滿足了之前類設計需求需要在主線程中反饋執行結果.剩下的
private final WorkerRunnable mWorker;
private final FutureTask mFuture;
private volatile Status mStatus = Status.PENDING;
private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
將放在通過調用類的構造函數以及執行的流程中講訴.以上我們將AsyncTask的屬性分析完畢,接下來通過實例調用過程來進行源碼旅程.Demo的例子可以查看Github.
我們創建一個AsyncTask的子類TimeConsumingTask,模擬后臺耗時的操作以及反饋結果給UI線程.
public class TimeConsumingTask extends AsyncTask {
private static String TAG = "TimeConsumingTask";
private WeakReference mHandler;
public static final String TAG_RESULT = "result";
private static volatile int sExecutionCount ;
public TimeConsumingTask(Handler handler) {
mHandler = new WeakReference(handler);
}
@Override
protected void onPreExecute() {
Log.d(TAG, "onPreExecute");
}
@Override
protected Boolean doInBackground(Integer... params) {
Log.d(TAG,"execute count is :"+ ++sExecutionCount);
for (Integer num : params) {
try {
Thread.sleep(num * 1000);
//will call onProgressUpdate()
publishProgress(num);
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
for (Integer value : values) {
Log.d(TAG, "onProgressUpdate result: " + value);
if (null != mHandler.get()) {
Message message = new Message();
message.what = value;
mHandler.get().sendMessage(message);
}
}
}
@Override
protected void onPostExecute(Boolean aBoolean) {
Log.d(TAG, "onPostExecute result: " + aBoolean);
if (null != mHandler.get()) {
Message message = new Message();
message.getData().putBoolean(TAG_RESULT, aBoolean);
mHandler.get().sendMessage(message);
}}}
這里構造方法我是通過傳入一個弱引用的主線程的handler進行運行結果的反饋,雖然只是demo還是希望寫的嚴謹些.子類會率先調用父類的構造方法,此時我們來關注AsyncTask的構造方法,里面包含了剛才未說明的兩個屬性WorkerRunnable和FutureTask,FutureTask是java1.5引入的api,源碼說明是A cancellable asynchronous computation.具體的可以建議讀者去好好學習下,當下android很多流行的開源庫中實現都離不開FutureTask,這里就不做介紹.我們來看AsyncTask的構造函數:
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
public AsyncTask() {
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
};
mFuture = new FutureTask(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);
}}};
}
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}}
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
我們可以看到WorkerRunnable是實現Callable在call方法中修改了運行標識|設置線程優先級|以及真正的運行doInBackground(Param)的方法,以及在運行結束后調用postResult(result).其中 Binder.flushPendingCommands();這句的調用可能很多朋友不甚熟悉,可以點擊方法跳轉至源碼查看說明,這里留個懸念.在FutureTask的主要是重寫了done()的回調進行如果是未調用后臺方法就結束了異步任務的判斷,并拋出異常,此處可以發現好的api是在方法命名中就清楚的告訴了閱讀者意圖.此處的getHandler()就是通過lazyInit的方式獲取剛才說到的變量InternalHandler進行通信.
我們看完了構造方法,現在來看AsyncTask的調用方法,AsyncTask的api設計十分簡單,可調用方法execute(Params...)/cancel(boolean mayInterruptIfRunning)/...,接下來看下一般繼承AsyncTask一般要重寫的四個方法:onPreExecute(),doInBackground(Param...), onProgressUpdate(Progress... values) ,onPostExecute(Result result),接下來會講解這四個方法在源碼中被調用的時機.我們先看下核心方法execute(Params...).進入源碼:
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask 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;
}
在方法頭加入了google annotation@MainThread,如果還不了解google annotation的朋友也可以上官方開發者網站上搜索進行了解.說明execute方法需要運行在主線程中.execute的方法調用了executeOnExecutor并傳入剛才看到的sDefaultExecutor也就是SERIAL_EXECUTOR以及AsyncTask中三個泛型中的第一個Param作為參數.這里在executeOnExecutor運行中對Status進行了檢查,如果非PENDING態則拋出異常.Status只有PENDING/RUNNING/FINISHED,三種狀態,初始化的時候是PENDING態,這也是我們不能對同一個task反復調用execute的原因.更改狀態之后就調用了 onPreExecute();所以開發者繼承AsyncTask重寫的四大方法中第一個onPreExecute()是運行在主線程中.
exec.execute(mFuture);
AsyncTask的sDefaultExecutor開始執行我們在構造函數中初始化的futureTask了.此處讓我們將目光轉向sDefaultExecutor的默認賦值SERIAL_EXECUTOR:
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);}}}
好的代碼總是簡潔明快,通過ArrayDeque 存儲傳入的任務,并在scheduleNext通過THREAD_POOL_EXECUTOR.execute(task)的方式進行任務調用,這里就不多做闡述了,這個Executor做了和它類名一樣的實行就是序列化執行列表中的任務.SERIAL_EXECUTOR是在3.0版本后加入的,說明3.0版本后AsyncTask默認不是并行化執行任務而是順序執行.也就是說在3.0之前的AsyncTask可以同時有5個任務在執行,而3.0之后的AsyncTask同時只能有1個任務在執行.
之前在文章中已經講述在WorkerRunnable的call()方法中調用了耗時操作方法doInBackground(Params...),那在后臺執行的過程中更新UI的方法onProgressUpdate(Progress... values)又是怎么被調用的,根據官方文檔在doInBackground方法中需要post結果到主線程的時候會調用publishProgress(Progress...)方法.而這個方法和主線程有關系我相信讀者已經有一個概念該方法中是通過往IntervalHandler發出消息來實現:
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult(this, values)).sendToTarget();
}
}
這邊提一下方法頭的googleAnnotation @WorkerThread說明該方法只能在工作線程中被調用.
Demo地址:在使用Demo時候可以通過DDMS的Thread監控功能進行AsyncTask的線程池監控驗證.
這里只是拋磚引玉寫一篇AsyncTask的源碼分析,希望各位大神能熱心參與android異步編程的討論.
Android異步一些輕量實現的討論文章:https://medium.com/@ali.muzaffar/handlerthreads-and-why-you-should-be-using-them-in-your-android-apps-dc8bf1540341#.6de7zdbii