主線程
在程序啟動的時候,就調(diào)用Looper.prepareMainLooper方法
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
public static void prepare() {
prepare(true);
}
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));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
可以看到上面的代碼主要做了這么幾件事情:
- 初始化了一個Looper對象剩岳,在構(gòu)造方法中初始化了一個MessageQueue勒葱,同時獲取了當前線程并賦值給變量mThread ;用于后期使用時比較當前執(zhí)行線程是否為Looper初始化線程
- 主線程中的Looper初始化傳入了一個
quitAllowed=false
,這說明主線程是不可以隨便中斷的康愤; - 將初始化的Looper對象存入到線程本地變量sThreadLocal中玷犹;如果本地已經(jīng)存在則說明初始化過了一次混滔,則拋出運行時異常;說明主線程的Looper只能prepare一次歹颓;
- 因為只能prepare一次坯屿,所以說明構(gòu)造方法只能執(zhí)行一次,則對應(yīng)的MessageQueue也只有一個巍扛;
- 然后執(zhí)行l(wèi)oop领跛;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
...
msg.recycleUnchecked();
}
從MessageQueue中獲取消息queue.next()
;沒有消息則循環(huán)中斷撤奸;
有消息則通過msg.target.dispatchMessage(msg)
轉(zhuǎn)發(fā)出去吠昭;最后釋放消息msg.recycleUnchecked()
喊括;
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
...
//掛起條件
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//掛起關(guān)鍵點
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果當前是異步消息,找到下一個異步消息矢棚,作用是啥暫時不知道
if (msg != null && msg.target == null) {
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
if (mQuitting) {
dispose();
return null;
}
...
}
...
nextPollTimeoutMillis = 0;
}
}
在MessageQueue中獲取消息也就幾個關(guān)鍵點:
- for循環(huán)遍歷消息隊列
- 判斷當前是否沒有消息或者沒有需要立刻執(zhí)行的消息郑什,則nextPollTimeoutMillis=-1或者>0;然后掛起蒲肋,CPU暫時釋放資源(Linux的epoll管道機制蘑拯,不是很懂,就知道此時作用就是CPU暫時釋放資源):
nativePollOnce(ptr, nextPollTimeoutMillis);
- 因為隊列里的同步消息是按照執(zhí)行時間有序排列兜粘,所以如果要喚醒CPU申窘,則除非第一個Message的執(zhí)行時間到了或者突然有消息插入且是立刻執(zhí)行的;
消息遍歷已經(jīng)有了妹沙,開始發(fā)送消息偶洋,讓消息隊列里面有消息,到了Handler距糖;
三種方式發(fā)消息玄窝,對應(yīng)著三種方式接收消息
第一種:new Handler(Callback)
第二種:new Handler();handler.sendXXXMessage()
第三種:Handler().post(new Runnable())
public void dispatchMessage(Message msg) {
//第三種
if (msg.callback != null) {
handleCallback(msg);
} else {
//第一種
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//第二種
handleMessage(msg);
}
}
其中第一種的返回值還影響到第二種的是否執(zhí)行;
發(fā)送消息最終走queue.enqueueMessage(msg, uptimeMillis)
,
在里面主要對消息做了檢測:
- 是否是同步消息
- 該消息是否已經(jīng)使用過一次
- 是否消息隊列已經(jīng)結(jié)束循環(huán)并退出
- 判斷該消息的執(zhí)行時間是否需要喚醒CPU(假設(shè)當前掛起悍引,沒掛起則正常流程走)
這樣從消息隊列的創(chuàng)建恩脂、循環(huán)檢測、消息入隊趣斤、的流程都有了俩块;
子線程
子線程和主線程的區(qū)別在于主線程已經(jīng)通過Looper.prepareMainLooper()
初始化過了Looper,而且調(diào)用loop開始循環(huán)消息隊列浓领;而子線程需要自己創(chuàng)建一個綁定當前子線程的Looper玉凯,需要prepare,然后loop联贩;剩下的都一樣漫仆。
經(jīng)典問題
Handler 是何時調(diào)度線程的:這個在了解之后其實已經(jīng)不是問題了,因為Handler是依附于Looper創(chuàng)建泪幌,Looper在哪個線程創(chuàng)建盲厌,則Handler就在哪個線程;也就是按照我們的使用慣例祸泪,在主線程中初始化一個Handler吗浩,然后再子線程中發(fā)消息,因為是在主線程中創(chuàng)建的handler没隘,所以消息是發(fā)送到了主線程的MessageQueue中懂扼,然后分發(fā)也是到主線程中對應(yīng)的handler回調(diào);
另一種使用方法右蒲,在子線程中創(chuàng)建Looper微王,然后創(chuàng)建基于子線程的Handler屡限,那么在主線程中使用Handler發(fā)消息,則收到的消息就在子線程中的Handler的回調(diào)中炕倘。
最后借用一張圖: