【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】java
Android異步處理機制一直都是Android的一個核心,也是應用工程師面試的一個知識點。前面咱們分析了Handler異步機制原理(不瞭解的能夠閱讀個人《Android異步消息處理機制詳解及源碼分析》文章),這裏繼續分析Android的另外一個異步機制AsyncTask的原理。android
當使用線程和Handler組合實現異步處理時,當每次執行耗時操做都建立一條新線程進行處理,性能開銷會比較大。爲了提升性能咱們使用AsyncTask實現異步處理(其實也是線程和handler組合實現),由於其內部使用了java提供的線程池技術,有效的下降了線程建立數量及限定了同時運行的線程數,還有一些針對性的對池的優化操做。因此說AsyncTask是Android爲咱們提供的方便編寫異步任務的工具類。面試
【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】api
先看下使用AsyncTask模擬下載的效果圖:markdown
看下代碼,以下:異步
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new TestAsyncTask(this).execute();
}
static final class TestAsyncTask extends AsyncTask<Void, Integer, Boolean> {
//如上三個泛型參數從左到右含義依次爲:
//1. 在執行AsyncTask時須要傳入的參數,可用於在後臺任務中使用。
//2. 後臺任務執行時,若是須要在界面上顯示當前的進度,則使用這個。
//3. 當任務執行完畢後,若是須要對結果進行返回,則使用這個。
private Context mContext = null;
private ProgressDialog mDialog = null;
private int mCount = 0;
public TestAsyncTask(Context context) {
mContext = context;
}
//在後臺任務開始執行之間調用,用於進行一些界面上的初始化操做
protected void onPreExecute() {
super.onPreExecute();
mDialog = new ProgressDialog(mContext);
mDialog.setMax(100);
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.show();
}
//這個方法中的全部代碼都會在子線程中運行,咱們應該在這裏去處理全部的耗時任務
protected Boolean doInBackground(Void... params) {
while (mCount < 100) {
publishProgress(mCount);
mCount += 20;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
//當在後臺任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mDialog.setProgress(values[0]);
}
//當後臺任務執行完畢並經過return語句進行返回時,這個方法就很快會被調用
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean && mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
}
}
能夠看見Android幫咱們封裝好的AsyncTask仍是很方便使用的,我們不作過多說明。接下來直接分析源碼。async
【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】ide
經過源碼能夠發現AsyncTask是一個抽象類,因此咱們在在上面使用時須要實現它。函數
那怎麼下手分析呢?很簡單,咱們就依據上面示例的流程來分析源碼,具體以下。工具
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */
public AsyncTask() {
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));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
看見註釋沒有,AsyncTask的實例化只能在UI線程中。而後整個構造函數就只初始化了兩個AsyncTask類的成員變量(mWorker和mFuture)。mWorker爲匿名內部類的實例對象WorkerRunnable(實現了Callable接口),mFuture爲匿名內部類的實例對象FutureTask,傳入了mWorker做爲形參(重寫了FutureTask類的done方法)。
正如上面實例同樣,獲得AsyncTask實例化對象以後就執行了execute方法,因此看下execute方法的源碼,以下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
能夠看見,execute調運了executeOnExecutor方法,executeOnExecutor方法除過傳入了params形參之外,還傳入了一個static的SerialExecutor對象(SerialExecutor實現了Executor接口)。繼續看下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;
}
首先判斷AsyncTask異步任務的狀態,當處於RUNNING和FINISHED時就報IllegalStateException非法狀態異常。由此能夠看見一個AsyncTask的execute方法只能被調運一次。接着看見17行onPreExecute();沒有?看下這個方法源碼,以下:
/** * Runs on the UI thread before {@link #doInBackground}. * * @see #onPostExecute * @see #doInBackground */
protected void onPreExecute() {
}
空方法,並且經過註釋也能看見,這不就是咱們AsyncTask中第一個執行的方法嗎?是的。
回過頭繼續往下看,看見20行exec.execute(mFuture);代碼沒?exec就是形參出入的上面定義的static SerialExecutor對象(SerialExecutor實現了Executor接口),因此execute就是SerialExecutor靜態內部類的方法嘍,在執行execute方法時還傳入了AsyncTask構造函數中實例化的第二個成員變量mFuture。咱們來看下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);
}
}
}
在源碼中能夠看見,SerialExecutor在AsyncTask中是以常量的形式被使用的,因此在整個應用程序中的全部AsyncTask實例都會共用同一個SerialExecutor對象。
接着能夠看見,SerialExecutor是使用ArrayDeque這個隊列來管理Runnable對象的,若是咱們一次性啓動了不少個任務,首先在第一次運行execute()方法的時候會調用ArrayDeque的offer()方法將傳入的Runnable對象添加到隊列的最後,而後判斷mActive對象是否是等於null,第一次運行是null,而後調用scheduleNext()方法,在這個方法中會從隊列的頭部取值,並賦值給mActive對象,而後調用THREAD_POOL_EXECUTOR去執行取出的取出的Runnable對象。以後若是再有新的任務被執行時就等待上一個任務執行完畢後纔會獲得執行,因此說同一時刻只會有一個線程正在執行,其他的均處於等待狀態,這就是SerialExecutor類的核心做用。
咱們再來看看上面用到的THREAD_POOL_EXECUTOR與execute,以下:
public abstract class AsyncTask<Params, Progress, Result> {
......
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);
/** * 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);
......
}
看見沒有,實質就是在一個線程池中執行,這個THREAD_POOL_EXECUTOR線程池是一個常量,也就是說整個App中不論有多少AsyncTask都只有這一個線程池。也就是說上面SerialExecutor類中execute()方法的全部邏輯就是在子線程中執行,注意SerialExecutor的execute方法有一個Runnable參數,這個參數就是mFuture對象,因此咱們看下FutureTask類的run()方法,以下源碼:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
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);
}
}
看見沒有?第7行的c = callable;其實就是AsyncTask構造函數中實例化FutureTask對象時傳入的參數mWorker。12行看見result = c.call();
沒有?其實就是調運WorkerRunnable類的call方法,因此咱們回到AsyncTask構造函數的WorkerRunnable匿名內部內中能夠看見以下:
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()方法的參數裏面,咱們能夠看見doInBackground()方法。因此這驗證了咱們上面例子中使用的AsyncTask,首先在主線程執行onPreExecute方法,接着在子線程執行doInBackground方法,因此這也就是爲何咱們能夠在doInBackground()方法中去處理耗時操做的緣由了,接着等待doInBackground方法耗時操做執行完畢之後將返回值傳遞給了postResult()方法。因此咱們來看下postResult這個方法的源碼,以下:
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
先看下這個getHandler拿到的是哪一個Handler吧,以下:
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;
}
}
}
看見沒有,拿到的是MainLooper,也就是說在在UI線程中的Handler(不清楚的請閱讀《Android異步消息處理機制詳解及源碼分析》文章)。因此上面的方法其實就是將子線程的數據發送到了UI來處理,也就是經過MESSAGE_POST_RESULT在handleMessage來處理。因此咱們繼續看handleMessage中的result.mTask.finish(result.mData[0]);就會發現finish的代碼以下:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
看見沒有?依據返回值true與false回調AsyncTask的onPostExecute或者onCancelled方法。
到此是否是會好奇onProgressUpdate方法啥時候調運的呢?繼續往下看能夠發現handleMessage方法中的MESSAGE_POST_PROGRESS不就是回調咱們UI Thread中的onProgressUpdate方法嗎?那怎麼樣才能讓他回調呢?追蹤MESSAGE_POST_PROGRESS消息你會發現以下:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
額,沒意思了。這不就是咱們上面例子中的在子線程的doInBackground耗時操做中調運通知回調onProgressUpdate的方法麼。
看見沒有,AsyncTask的實質就是Handler異步消息處理機制(不清楚的請閱讀《Android異步消息處理機制詳解及源碼分析》文章),只是對線程作了優化處理和封裝而已。
【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】
接觸Android比較久的可能都知道,在Android 3.0以前是並無SerialExecutor這個類的(上面有分析)。那些版本的代碼是直接建立了指定大小的線程池常量來執行task的。其中MAXIMUM_POOL_SIZE = 128;,因此那時候若是咱們應用中一個界面須要同時建立的AsyncTask線程大於128(批量獲取數據,譬如照片瀏覽瀑布流一次加載)程序直接就掛了。因此當時的AsyncTask由於這個緣由臭名昭著。
回過頭來看看如今高版本的AsyncTask,是否是沒有這個問題了吧?由於如今是順序執行的。並且更勁爆的是如今的AsyncTask還直接提供了客戶化實現Executor接口功能,使用以下方法執行AsyncTask便可使用自定義Executor,以下:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
......
return this;
}
能夠看出,在3.0以上版中AsyncTask已經不存在那個臭名昭著的Bug了,因此能夠放心使用了,媽媽不再用擔憂個人AsyncTask出Bug了。
【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】
前面文章也分析過Handler了,這裏也分析了AsyncTask,如今把他們兩拽一塊兒來比較比較。具體以下:
AsyncTask是對Handler與Thread的封裝。
AsyncTask在代碼上比Handler要輕量級別,但實際上比Handler更耗資源,由於AsyncTask底層是一個線程池,而Handler僅僅就是發送了一個消息隊列。可是,若是異步任務的數據特別龐大,AsyncTask線程池比Handler節省開銷,由於Handler須要不停的new Thread執行。
AsyncTask的實例化只能在主線程,Handler能夠隨意,只和Looper有關係。
到此整個Android的AsyncTask已經分析完畢,相信你如今對於AsyncTask會有一個很深刻的理解與認識了。
【工匠若水 http://blog.csdn.net/yanbober 轉載煩請註明出處,尊重分享成果】