歡迎轉載,請標明出處:http://blog.csdn.net/a_long_/article/details/51635378 ,本文出自【阿隆
a_long_】
本文目錄:
1.Handler的兩個作用
2.爲什麼創建Handler之前必須要調用Looper的prepare()方法?
3.爲什麼所以每個線程對應一個looper對象,同樣對應一個messageQueue?
4.爲什麼子線程不更新UI,也會需要用到Looper?
5.如果子線程需要用Looper,怎麼使用?
6.爲什麼主線程不需要prepare()?
7.Handler的構造函數裏還有一個async是幹什麼的?
8.Handler是在哪個線程執行的post和handleMessage?
9.Handler裏面的callback又是什麼鬼?
10.HandlerThread的run方法已經被寫死了,那還怎麼在子線程中幹活:
11. MessageQueue如何根據規定的時間對信息執行調度,如何在無信息的時候阻塞,有信息的時候喚醒:
1.Handler的兩個作用
1:調度消息:在未來執行某操作(放到Looper裏有順序的執行)。
2:在別的線程(而不是自己的線程)執行某操作。(子線程耗時操作後利用Hadnler在主線程更新UI)
源碼中的解釋:
There are two main uses for a Handler: (1) to schedule messages and
runnables to be executed as some point in the future; and (2) to enqueue
an action to be performed on a different thread than your own.
2.爲什麼創建Handler之前必須要調用Looper的prepare()方法?
由於:
原因1:Handler的構造函數有很多種,但最終都會直接去拿當前線程的sThreadLocal裏面的Looper實例(sThreadLocal.get();),如果沒有實例就報異常。
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
原因2:Looper的構造函數是private,所以不能直接創建Looper對象,只能通過prepare()方法。
所以導致:創建Handler之前必須要調用Looper的prepare()方法。
3.爲什麼所以每個線程對應一個looper對象,同樣對應一個messageQueue?
Looper在創建實例的時候,會和當前線程綁定且創造出一個對應的MessageQueue,構造函數如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
由於Looper對象只能創建一次,原因如下:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
由於每個線程都有自己獨立的ThreadLocal,所以每個線程對應一個looper對象,同樣只對應一個messageQueue。4.爲什麼子線程不更新UI,也會需要用到Looper?
應用實例:大量圖片的加載工具:比如GridView實現手機的相冊功能,一般會用到LruCache,線程池,任務隊列等;那麼異步消息處理可以用哪呢?
1、用於UI線程當Bitmap加載完成後更新ImageView
2、在圖片加載類初始化時,我們會在一個子線程中維護一個Loop實例,當然子線程中也就有了MessageQueue,Looper會一直在那loop停着等待消息的到達,當有消息到達時,從任務隊列按照隊列調度的方式(FIFO,LIFO等),取出一個任務放入線程池中進行處理。
簡易的一個流程:當需要加載一張圖片,首先把加載圖片加入任務隊列,然後使用loop線程(子線程)中的hander發送一個消息,提示有任務到達,loop()(子線程)中會接着取出一個任務,去加載圖片,當圖片加載完成,會使用UI線程的handler發送一個消息去更新UI界面。
(此例摘自鴻洋老師)
總結:子線程的Looper是維護消息隊列、調度用的,使得線程池有序運作。如果要更新UI,還是需要主線程的Handler。
5.如果子線程需要用Looper,怎麼使用?
方法一:Looper類裏面的描述,非常清楚:
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
<pre>
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}</pre>
方法二:使用HandlerThread,在創建Handler的時候給一個參數Handler handler = new Handler(handlerThread.getLooper())。
原理和方法一是一樣的,只是HandlerThread幫忙封裝了prepare(), Looper.loop();方法,所以不用自己來調用。
源碼如下:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
6.爲什麼主線程不需要prepare()?
因爲在創建ActivityThread的時候調用了prepareMainLooper(),這時已經創建了,無需自己再調用。
源碼如下:
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
7.Handler的構造函數裏還有一個async是幹什麼的?
這個參數默認是false,也就是默認Handler是非異步(同步)的,也就是Looper一個一個往外拿Message,這才能保證UI線程不報異常。
但是如果一個Looper的MessageQueue裏都可以接受同步,那麼把這個參數設成true可以提高效率。
不過實際上這樣的構造函數都被@hide了,所以實際上所有的Handler都是同步的。
Handler類對async參數的解釋:
* Handlers are synchronous by default unless this constructor is used to make
* one that is strictly asynchronous.
*
* Asynchronous messages represent interrupts or events that do not require global ordering
* with respect to synchronous messages. Asynchronous messages are not subject to
* the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
*
* @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
* each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
8.Handler是在哪個線程執行的post和handleMessage?
都是在Handler所在的線程。比如Handler一般在主線程創建,那麼post的runnable對象以及handleMessage都是在主線程工作的,容易造成ANR。應該建立子線程,在run函數中通過主線程handler的引用來sendMessage,這樣就不會耗時。或者在子線程的Handler中的handleMessage中循環調用給自己發消息並且利用主線程的Handler更新UI,也可以實現子線程做耗時任務最終UI線程更新UI的目的。
handler的發送message和task只是往某一個looper的MessageQueue注入一項。
Looper是和thread結合的,就是一個事件循環。當該線程回到其事件循環時,之前注入的項就會得到執行。
至於在哪個線程裏執行,要看handler發送到哪個線程的looper了,創建handler時都會綁定一個looper(ui線程是自動綁定的),handler發送的message等都會在該looper的線程得到處理。
如果是想在HandlerThread執行,那麼在創建Handler的時候new Hadnler(handlerThread.getLooper())即可。
9.Handler裏面的callback接口又是什麼鬼?
根據源碼裏面的說明:Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.
可以用callback實例化Handler而不必自己實現Handler的子類。
所以和自己實現Handler的子類的效果是相同的,只是方便一些。
10.HandlerThread的run方法已經被寫死了,那還怎麼在子線程中幹活:
用子線程的Handler的方法,有sandMessage、sendMessageAtTime等,比如可以在handleMessage裏面加入sendMessageAtTime(message,1000),那麼子線程就會每秒鐘去幹活。
11.MessageQueue如何根據規定的時間對信息執行調度,如何在無信息的時候阻塞,有信息的時候喚醒:
這不是隊列,而是一個鏈表,放入鏈表中合適的位置用enqueueMessage函數:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p爲null(代表MessageQueue沒有消息) 或者msg的觸發時間是隊列中最早的, 則進入該該分支
msg.next = p;
mMessages = msg;
needWake = mBlocked; //當阻塞時需要喚醒
} else {
//將消息按時間順序插入到MessageQueue。一般地,不需要喚醒事件隊列,除非
//消息隊頭存在barrier,並且同時Message是隊列中最早的異步消息。
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//會從隊列頭開始遍歷
for (;;) {
prev = p;
p = p.next;
//直到找到消息應該插入的合適位置,以保證所有消息的時間順序。
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
MessageQueue是按照Message觸發時間的先後順序排列的,隊頭的消息是將要最早觸發的消息。當有消息需要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應該插入的合適位置,以保證所有消息的時間順序。
讀取下一條message用MessageQueue的next函數:
@Override
Message next() {
final long ptr = mPtr;
int pendingIdleHandlerCount = -1; // 循環迭代的首次爲-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//當消息Handler爲空時,查詢MessageQueue中的下一條異步消息msg,則退出循環。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//當異步消息觸發時間大於當前時間,則設置下一次輪詢的超時時長
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 獲取一條消息,並返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//設置消息的使用狀態,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地獲取MessageQueue中的下一條即將要執行的消息
}
} else {
//沒有消息
nextPollTimeoutMillis = -1;
}
//當消息隊列爲空,或者是消息隊列的第一個消息時
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//沒有idle handlers 需要運行,則循環並等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
}
//當調用一個空閒handler時,一個新message能夠被分發,因此無需等待可以直接查詢pending message.
nextPollTimeoutMillis = 0;
}
}
參考:
guolin大神的Handler分析
廣泛的網上對Handler、Looper的源碼分析
Mars老師的教程
Handler詳細使用方法實例