Android 消息機(jī)制深入源碼分析 [ 一 ]
Android 消息機(jī)制之 ThreadLocal 深入源碼分析 [ 二 ]
Android 消息機(jī)制之 Looper 深入源碼分析 [ 三 ]
Android 消息機(jī)制之 Message 與消息對(duì)象池的深入源碼分析 [ 四 ]
Android 消息機(jī)制之 MessageQueue 深入源碼分析 [ 五 ]
Android 消息機(jī)制之初識(shí)Handler [ 六 ]
Android 消息機(jī)制之 Handler 發(fā)送消息的深入源碼分析 [ 七 ]
Android 消息機(jī)制之 MessageQueue.next() 消息取出的深入源碼分析 [ 八 ]
Android 消息機(jī)制之消息的其他處理深入源碼分析 [ 九 ]
Android 消息機(jī)制總結(jié) [ 十 ]
上一章學(xué)習(xí)了消息機(jī)制中的 ThreadLocal, 本章接著來學(xué)習(xí)消息機(jī)制中的 Looper. 開篇也是先拋出幾個(gè)問題.
1. 問題
- 可以在一個(gè)線程多次執(zhí)行 Looper. prepare() 嗎? 為什么 ?
- Looper.prepareMainLooper 是用來做什么的. 為什么我們?cè)谥骶€程可以直接使用 Handler, 而不需要調(diào)用 Looper. prepare() ?
- Looper.quit 與 Looper.quitSafely() 有什么區(qū)別.
2. 例
先來一個(gè)典型的關(guān)于 Looper 的例子. (因?yàn)楸菊轮环治?Looper, Handler 會(huì)放到后面章節(jié)分析.)
class LooperThread extends Thread{
public Handler mHandler;
public void run(){
//分析1
Looper.prepare();
mHandler = new Handler(){
public void HhandleMessage(Message msg){
Message msg = Message.obtain();
}
};
//分析 2
Looper.loop();
}
}
3. 分析 1
進(jìn)入 Looper.prepare()
方法. 代碼在 Looper.java
97行.
public static void prepare() {
prepare(true);
}
- 官方注釋:
初始化當(dāng)前線程和
Looper
, 這樣可以在實(shí)際開始啟動(dòng)循環(huán)(Loop()
) 之前創(chuàng)建一個(gè)Handler
, 并且關(guān)聯(lián)一個(gè)Looper
. 要確保最先調(diào)用這個(gè)方法, 然后才調(diào)用loop()
, 并且通過調(diào)用quit
結(jié)束.
這也正對(duì)應(yīng)了我們例子中寫的, 要先調(diào)用 Looper.prepare()
, 然后創(chuàng)建一個(gè) Handler
, 最后才調(diào)用 Looper.loop()
啟動(dòng)循環(huán).
繼續(xù)回到 public static void prepare()
, 在方法內(nèi)部又調(diào)用了一個(gè) private static void prepare(boolean quitAllowed)
, 注意, 這兩個(gè)方法名是相同的, 不過一個(gè)是 public
無參的, 一個(gè)是 private
有參的. 而我們調(diào)用的是 public
無參的, 在這個(gè)方法內(nèi)部, Android 系統(tǒng)又調(diào)用了 private
有參的, 并且傳入的參數(shù)為 true
, 實(shí)際上這個(gè)參數(shù)一直向下傳遞到了 MessageQueue
的構(gòu)造函數(shù)中, 表示允許退出消息隊(duì)列. 也就是說我們?cè)谕獠空{(diào)用 Looper.prepare()
后, 系統(tǒng)為我們創(chuàng)建的消息隊(duì)列是允許退出的.
?
進(jìn)入到 private static void Looper.prepare(true)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<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
, 所以我們需要先學(xué)習(xí)了第二章, 再來看這個(gè)就會(huì)明白是什么意思了. 這個(gè)sThreadLocal
中存儲(chǔ)的是Looper
對(duì)象.
調(diào)用ThreadLocal.get()
看獲取到的值是否為null
, 如果為不為null
說明當(dāng)前線程已經(jīng)創(chuàng)建過Looper
對(duì)象了. 那么就會(huì)直接拋出異常. [每個(gè)線程只能執(zhí)行一次 Looper], 那么這就是問題1 的答案,
如果獲取到值為null
, 說明當(dāng)前線程還未創(chuàng)建Looper
, 就會(huì)創(chuàng)建一個(gè)Looper
對(duì)象, 并放到ThreadLocal
中.
接著我們進(jìn)入 Looper
的構(gòu)造函數(shù), 看看都做了些什么.
?
進(jìn)入到 Looper(boolean quitAllowed)
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 解析
看到構(gòu)造函數(shù)內(nèi)部創(chuàng)建了一個(gè)
MessageQueue
, 傳入boolean quitAllowed
設(shè)置是否允許退出, 然后賦值給本地變量mQueue
, 然后獲取當(dāng)前線程, 也賦值給本地變量mThread
.
這樣這個(gè)創(chuàng)建的
Looper
內(nèi)就持有MessageQueue
消息隊(duì)列 與Thread
當(dāng)前線程的引用. 也就實(shí)現(xiàn)了 Looper 與 MessageQueue 和 Thread 的關(guān)聯(lián) ()
通過 Looper
的構(gòu)造函數(shù)可以知道, Looper
是無法被直接創(chuàng)建的, (構(gòu)造函數(shù)是 private
類型的). 我們必須通過 Looper
的兩個(gè)靜態(tài)方法, prepare()/prepareMainLooper()
來間接的創(chuàng)建. 那么 prepareMainLooper
是用來做什么的呢, 一起來看一下
?
進(jìn)入到 prepareMainLooper()
Looper.java 114 行
public static void prepareMainLooper() {
//設(shè)置不允許退出的 Looper
prepare(false);
synchronized (Looper.class) {
//主線程有且只能調(diào)用一次 prepareMainLooper()
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//賦值給 sMainlooper
sMainLooper = myLooper();
}
}
- 官方注釋
初始化當(dāng)前線程的
looper
. 并且標(biāo)記為一個(gè)程序的主Looper
, 由Android
環(huán)境來創(chuàng)建應(yīng)用程序的主Looper
, 因此這個(gè)方法不能由自己調(diào)用,只能是Android
自己調(diào)用
什么意思呢, 簡(jiǎn)單來說就是初始化主線程的 Looper
, 而且只能由 Android
系統(tǒng)調(diào)用.
- 解析
一開始也調(diào)用了
private static void prepare(boolean quitAllowed)
傳入了 false, 結(jié)合上面的分析, 是創(chuàng)建了一個(gè)Looper
對(duì)象, 然后set
到當(dāng)前線程的ThreadLocal
中, 并且在Looper
構(gòu)造函數(shù)中創(chuàng)建的MessageQueue
是無法退出的.
接著做了sMainLooper
的非空判斷,sMainLooper
看名字就知道這是什么了, 就是主線程的 Looper, 如果非空, 就直接拋出異常, 所以這個(gè)sMainLooper
一開始必須是空, 因?yàn)橹骶€程有且只能調(diào)用一次prepareMainLooper()
, 如果sMainLooper
非空, 說明prepareMainLooper()
已經(jīng)被系統(tǒng)調(diào)用過了. 為空, 就調(diào)用myLooper()
方法, 將返回的Looper
對(duì)象賦值給sMainLooper
. 那么prepareMainLooper()
是在什么時(shí)候被調(diào)用的呢, 答案是在ActivityThread,java
中的main
方法.
那么問題 2 的答案也有了:
prepareMainLooper()
是用來為主線程創(chuàng)建Looper
的, 系統(tǒng)默認(rèn)為我們創(chuàng)建好了, 所以我們可以在主線程中直接使用Handler
?
進(jìn)入到 myLooper()
Looper.java 的 254行
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- 解析
從當(dāng)前線程的
ThreadLocal
中取出Looper
對(duì)象并返回. 這里的sThreadLocal.get();
與prepare(boolean)
方法里面的sThreadLocal.set(new Looper( quitAllowed))
對(duì)應(yīng)的, 一個(gè)設(shè)置值一個(gè)取值.
現(xiàn)在完整的分析 1, 已經(jīng)分析完了, 主要就是為當(dāng)前線程創(chuàng)建一個(gè)Looper
對(duì)象放入到當(dāng)前線程的 ThreadLocal
中, 并同時(shí)持有 MessageQueue
對(duì)象與當(dāng)前線程對(duì)象. 其中創(chuàng)建的消息隊(duì)列, 是可以退出的. 也知道了我們?yōu)槭裁纯梢栽谥骶€程直接使用 Handler
, 就是因?yàn)樵?code>ActivityThread.main 方法中調(diào)用了 Looper.prepareMainLooper()
方法, 系統(tǒng)已經(jīng)為主線程創(chuàng)建好了 Looper
.
Looper
已經(jīng)創(chuàng)建好了, 那么接下來就是開始循環(huán)了. 接下來開始看分析 2 Looper.loop()
.
?
4. 分析 2
Looper.java 137 行, 進(jìn)入 Looper.loop()
方法
public static void loop() {
//獲取當(dāng)前線程 ThreadLocal 存儲(chǔ)的 Looper
final Looper me = myLooper();
if (me == null) {
//沒有 Looper 直接拋出異常
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取當(dāng)前 Looper 持有的消息隊(duì)列
final MessageQueue queue = me.mQueue;
//確保權(quán)限檢查基于本地進(jìn)程, 而不是基于最初調(diào)用進(jìn)程
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進(jìn)入 loop 主循環(huán)方法
//一個(gè)死循環(huán), 不聽的處理消息隊(duì)列中的消息,消息的獲取是通過 MessageQueue 的 next 方法
for (;;) {
//可能會(huì)阻塞
Message msg = queue.next();
//注: 這里的 msg = null, 并不是說沒消息了, 而是如果消息隊(duì)列正在關(guān)閉的情況下, 會(huì)返回 null.
if (msg == null) {
return;
}
...
//用于分發(fā)消息,調(diào)用 Message 的 target 變量(也就是 Handler)的 dispatchMessage方法處理消息
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
...
//將 Message 回收標(biāo)記后放入消息池.
msg.recycleUnchecked();
}
}
- 解析
簡(jiǎn)單來說就是 Loop() 進(jìn)入循環(huán)模式, 不斷的重復(fù)下面的操作, 直到?jīng)]有消息時(shí), 退出.
- 讀取
MessageQueue
的下一條數(shù)據(jù). 并賦值給Message
.- 調(diào)用把
Handler.dispatchMessage()
方法把Message
分發(fā)給相對(duì)應(yīng)的target
也就是Handler
.- 把分發(fā)后的
Message
打上標(biāo)記后放到消息池回收. 以便重復(fù)使用.
這里面有幾個(gè)重要的方法, 后面文章會(huì)解釋到.
-
MessageQueue.next
方法,讀取下一條Message
, 有阻塞 -
Message.Handler.dispatchMessage(Message msg)
方法:消息分發(fā) -
Message.recycleUnchecked()
方法:消息放入消息池
5. Looper.quit 與 Looper.quitSafely 問題 3 的答案.
上面只是開啟了循環(huán)方法, Looper 也提供了兩個(gè)退出循環(huán)的方法, 分別是 quit
與 quitSafely
我們調(diào)用的 Looper 的兩個(gè)退出循環(huán)的方法.
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
MessageQueue.quit(boolean safe)
void quit(boolean safe) {
//當(dāng) mQuitAllowed 為 false,表示不允許退出,強(qiáng)行調(diào)用 quit 會(huì)有異常
//mQuitAllowed 是在 Looper 構(gòu)造函數(shù)里面構(gòu)造 MessageQueue() 以參數(shù)傳入的.
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
//同步代碼塊
synchronized (this) {
//防止多次執(zhí)行退出操作
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//移除所有尚未觸發(fā)的所有消息
removeAllFutureMessagesLocked();
} else {
//移除所有消息
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
- quit: 會(huì)將消息隊(duì)列中的所有消息移除 (延遲消息和非延遲消息)
-
Looper
的quit()
方法內(nèi)部的本質(zhì)是調(diào)用MessageQueue
的quit(boolean safe)
方法. 傳入?yún)?shù)是false
.
-
- quitSafely: 會(huì)將消息隊(duì)列所有延遲消息移除, 非延遲消息則派發(fā)出去讓
Handler
處理.-
Looper
的quitSafely()
方法內(nèi)部調(diào)用的本質(zhì)也是MessageQueue
的quit(boolean safe)
方法. 只不過傳入?yún)?shù)是true
.
-
-
quitSafely
相比于quit
方法安全支出在于清空消息之前會(huì)派發(fā)出去所有的非延遲消息.
到這里. 相信對(duì) Looper 都有了自己的認(rèn)識(shí), 比如為什么要先調(diào)用 Looper.prepare(), 再調(diào)用 Looper.loop(), 以及為什么主線程可以直接使用Hander, 而不需要調(diào)用 Looper.prepare(). 還有 loop() 循環(huán)中是怎么發(fā)送消息的. 下一章接著分析消息機(jī)制中的 Message.