之前寫過一篇Handler的源碼解析文章,因為AsyncTask底層也是Handler實現的,所以不了解的可以先去了解下Handler。本文也會再次分析下Handler,畢竟它是android源碼中隨處可見的東東。
### 一、Handler的簡要分析
講Handler之前我們先講一下ThreadLocal的概念。簡單的說,ThreadLocal是介于局部變量和全局變量之間,可以在不同線程中互不干擾地存儲并提供數據。也就是說,變量a在A線程中值與在B線程中值可能是不同的。它通過get()和set()方法來獲取和存儲變量。
由于之前分析過Handler,在這里就大概的介紹,Handler底層是由MessageQueue和Looper去支撐。Looper可以理解為一個中介,負責論詢MessageQueue中的消息,并它消息傳給Handler進行處理。MessageQueue內部存儲結構是一個單鏈表,可以快速的插入和刪除。
在ActivityThread啟動過程中就會創建Looper并存放在ThreadLocal中,在Handler的構造方法中就會通過Looper.myLooper()去ThreadLocal中獲取該線程的Looper,接下來調用looper.prepare()方法,在該方法中創建MessageQueue,在 handler調用post或者sendMessage()時,就會調用enqueueMessage把消息存到單鏈表中,而looper.loop()方法其實就是一個死循環,一直在論詢MessageQueue,如果獲得到消息就調用target(其實就是handler)的dispatchMessage()并調用 handleMessage()進行消息處理。以上就是Handler的源碼流程,如果感興趣可以自行去跟一下。這里有一點需要強調下,就是在處理dispatchMessage()時,它的源碼如下:
~~~
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
~~~
只有當callback為空時才會調用handleMessage,否則調用callback.handleMessage()。這里也給我們提供了一個思路,可以通過callback來攔截消息,比如以下代碼:
~~~
private Handler H = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.what == 0x11){
return true;
}else{
return false;
}
}}) {
@Override
public void handleMessage(Message msg) {
//處理相應業務
};
~~~
這相當msg的what是0x11時,就不會再執行handleMessage了。
使用Handler有一點非常重要的,如果是在子線程使用,那需要手動的添加looper.prepare()和looper.loop()方法。而子線程handler的handleMessage也是在子線程中處理,這點需要處理。
### 二,HandlerThread
上面即然說到在子線程中使用Handler,那我們就來說下HandlerThread,HandlerThread其實就是一個封裝好Thread,它可以使用Handler,之所以不提倡在子線程中直接使用Handler是因為它存在并發的問題。也就是說Handler在創建時需要獲取Looper對象,但此時可能Thread還沒有跑到Looper.prepare()那一步,那就會報空指針異常,看到這里我們第一反應肯定是那就用wait()和notifyAll()來實現啊。是的,HandlerThread就是加入了這兩個元素,不信可以翻下源碼:
~~~
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
~~~
HandlerThread在調用getLooper時,如果還不存在,就wait(),它本身是一個Thread,它在run方法創建完就會調用notifyAll(),這樣getLooper就會繼續執行。就這么簡單。實現代碼如下:
~~~
HandlerThread thread = new HandlerThread("handler");
thread.start();
Handler h = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
//處理相應業務
};
};
h.sendEmptyMessage(0X11);
~~~
### 三,AsyncTask源碼分析
上面說了那么多,都是在消息處理機制,感覺有點跑題了,囧。。。接下來就來分析下AsyncTask,終于進入正題了。
**1.用法介紹**
它可以在線程池中執行后臺操作,并把進度結果傳遞給主線程。AsyncTask并不適合進行特別耗時的操作,對于耗時的任務來說建議使用線程池。
AsyncTask提供4個核心方法:
**onPreExecute()**:執行前執行。
**doInBackground(Params……params)**:執行任務,可通過publishProgress方法更新任務進度,publishProgress會調用onProgressUpdate,返回計算結果給onPostExecute。
**onProgressUpdate(Progress……values)**:在主線程中執行,當后臺任務執行進度發生改變時會調用。
onPostExecute(Result result):在任務執行之間調用。
它最基本的用法如下所示:
~~~
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls. length;
long totalSize = 0;
for ( int i = 0; i < count; i++) {
// totalSize += Downloader.downloadFile(urls[i]);
publishProgress(( int) ((i / ( float) count) * 100));
// Escape early if cancel() is called
if (isCancelled())
break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
// setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
// showDialog("Downloaded " + result + " bytes");
}
}
~~~
好啦 ,講完用法,來分析下它的源碼吧!!
**2.源碼分析**
從它的execute方法入手。
~~~
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
~~~
sDefaultExecutor就是一個線程池,一個進程的所有線程都會在這個串行的線程池中排隊。接著我們進入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;
}
~~~
可以發現onPreExecute()最先執行。這也應驗了我們之前的分析。接下來就調用了線程池執行代碼,來看下AsyncTask線程池的實現。
~~~
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);
}
}
}
~~~
系統會把Params封裝成FutureTask類,這是一個并發類,相當于Runnable,接著會在SerialExcutor的execute中執行,如果沒有正在活動的AsyncTask任務,就調用scheduleNext()執行下一個,從隊列中poll對象執行,直到完成。所以它是串行的(注意,3.0以后AsyncTask是串行執行的)。
AsyncTask有兩個線程池,(serialExecutor,THREAD_POOL_EXCUTOR),serialExecutor用于任務排隊,THREAD_POOL_EXCUTOR用于執行任務。著重來看下THREAD_POOL_EXCUTOR。
~~~
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;
~~~
~~~
<pre name="code" class="java">public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
~~~
有沒有很熟悉,上篇博客就分析過線程池的用法,它的核心線程是CPU數量+1,最大線程數是2CPU+1,超時時間是一秒。由于FutureTask的run方法會調用mWorkder的call方法,所以就調用如下代碼:
~~~
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));
}
};
~~~
先設置mTaskInvoked為true,表示當前任務己經被調用過了。然后執行doInBackground()方法,并把結果返回給postResult();所以子線程的業務就在doInBackground方法中執行。結果有兩種情況,后面會再講到,先來看下postResult如下:
~~~
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
~~~
看到這里就明白了,它的底層還是Handler。這里就不再分析Handler了。主要找到handler,看getHandler()的方法。
~~~
private static Handler getHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler();
}
return 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;
}
}
}
~~~
兩種情況,一種是運行結束,調用finish,一種是還沒結束,調用onProgressUpdate傳入進度值。是不是一下子就把三個方法串起來了。好了,到這里AsyncTask的源碼就分析完了。
總結下就是AsyncTask底層由Handler來完成線程的切換,內部由線程池來執行。有兩個線程池,一個用于任務排除,一個用于線程執行。
好啦 ,就寫到這吧。
- 前言
- Android底部tab與標題欄相結合
- Android免費獲取短信驗證碼
- android中Handler的源碼分析
- 詳解Fragment的傳值問題
- 詳談gson解析
- android新控件之toolbar,floatingActionButton,SnackBar,CollapsingToolbarLayout
- android自定義控件
- 淺談android的線程池
- Android的消息處理機制,AsyncTask源碼解析
- IPC——android進程間通信
- CoCos2d_android入門所需知道的一切
- Cocos2d_android你所需要知道的一切(下)
- Activity你需要知道的一切
- Activity啟動過程源碼分析
- Data Binding Guide——google官方文檔翻譯(上)
- Data Binding Guide——google官方文檔翻譯(下)
- android TextView實現跑馬燈效果
- android中生成excel
- Volley源碼解析
- LayoutInflater源碼解析
- android發送郵件
- android測試工具MonkeyRunner--google官網翻譯
- android View繪制源碼分析
- Android中Window添加View的底層原理
- 仿美團商品選購下拉菜單實現