咱們都知道,Android系統強制要求咱們將更新ui等操做放在主線程中進行,而網絡請求,讀取文件等耗時操做則一般會放到子線程中運行,所以,在Android開發中常常須要在不一樣的線程之間進行切換。而Android系統爲咱們提供了消息系統來進行異步消息的處理,所以咱們有必要了解一下Android消息系統的工做原理。java
咱們先來看一下Handler,MessageQueue與Looper三者之間的關係。首先,Handler對象負責發出一個消息,這個消息最終會被提交到一個MessageQueue之中,這個MessageQueue則是一個專門用來存儲Message的隊列集合。而Looper對象內部有一個無限循環,它會不斷的從這個MessageQueue中取出消息,並將其交給發出該消息的Handler進行處理。後端
須要注意的是,咱們能夠在一個線程中建立不少個Handler對象,可是每一個線程只會對應一個Looper和MessageQueue對象。Handler對象在初始化的時候會和該線程所對應的Looper及MessageQueue對象進行綁定。所以,Handler在哪一個線程中建立,它發出的消息最終就會在哪一個線程中執行。接下來咱們經過源碼來詳細的看一下它們的工做原理。markdown
先從消息系統的建立提及。Android中主線程的消息系統會在主線程啓動時默認被建立,而子線程的消息系統默認則不會被建立,咱們須要在子線程中手動調用Looper.prepare()和Looper.loop()這兩個靜態方法才能夠開啓子線程的消息系統。接下來咱們以主線程爲例看一下消息系統的建立過程。網絡
Android主線程的消息系統是在ActivityThread類的main方法中被建立的,咱們看一下main方法的代碼:數據結構
public static void main(String[] args) {
...
Looper.prepareMainLooper(); //先調用prepareMainLooper方法
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //而後調用loop方法
throw new RuntimeException("Main thread loop unexpectedly exited");//由於Looper.loop()其實是執行了一個無限循環,因此通常狀況下不會走到這句,除非出現異常致使循環中斷
}
複製代碼
咱們能夠看到,與子線程不一樣的是,主線程的消息系統在啓動時調用的是Looper.prepareMainLooper方法而非prepare方法。在調用完prepareMainLooper方法以後又調用了Looper.loop方法。咱們看一下prepareMainLooper方法:異步
public static void prepareMainLooper() {
prepare(false);//調用prepare方法
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//經過myLooper方法將建立好的looper對象賦值給sMainLooper全局對象
}
}
複製代碼
能夠看到prepareMianLooper方法中其實也是調用的prepare方法,prepare方法的源碼以下:async
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));
}
複製代碼
在prepare方法中,系統直接經過new關鍵字建立了一個Looper對象,並將這個Looper對象放在了一個名爲sThreadLocal的全局對象中。ide
這個sThreadLocal對象是一個定義在Looper中的類型爲ThreadLocal的全局對象,而且被static final所修飾。ThreadLocal是java所提供的一個類,咱們能夠經過ThreadLocal的set(T value)方法來給這個ThreadLocal對象設置一個變量,但值得注意的是,經過ThreadLocal來維護的變量是線程私有的,各個線程經過ThreadLocal.get()方法取得的對象都是獨立的,他們之間的操做都互不影響的。主線程將一個Looper對象設置給了一個ThreadLocal,其餘子線程是沒法經過這個ThreadLocal對象來獲取主線程的Looper對象的。所以,Android經過ThreadLocal來維護Looper對象,就作到了每一個線程對應一個獨立的Looper對象。oop
咱們再來看一下Looper的構造方法:post
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
複製代碼
可見Looper對象在初始化時直接建立了一個MessageQueue集合,並賦值給成員變量mQueue。所以MessageQueue對象被Looper對象所持有。
如今Looper和MessageQueue對象已經建立完成了。咱們再回到prepareMainLooper方法中。在經過prepare方法建立完Looper對象和MessageQueue對象後,系統又調用了Looper的myLooper方法,而myLooper方法返回的實際上是剛纔建立的該線程所獨有的Looper對象,這裏便是主線程所對應的Looper對象。系統將這個對象賦值給了一個全局變量sMainLooper,方便以後使用getMainLooper方法來直接拿取主線程的Looper對象。myLooper方法的代碼以下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
複製代碼
至此準備工做都已經完成了,系統只須要再經過Looper.loop方法讓消息系統運行起來便可,loop方法的源碼以下:
public static void loop() {
final Looper me = myLooper(); //獲取looper對象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //獲取MessageQueue對象
...
for (;;) {
Message msg = queue.next(); // 從MessageQueue對象中獲取一個消息
if (msg == null) {
return;
}
...
try {
msg.target.dispatchMessage(msg);//將這個消息分發給相應的Handler進行處理
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
複製代碼
loop方法的代碼較多,爲了便於理解,省去了部分代碼。首先經過myLooper方法獲取當前線程對應的Looper對象,而後又經過me.mQueue拿取了looper對象內部的MessageQueue。以後開啓了一個無限循環,在循環中,首先經過queue.next()取出MessageQueue中存儲的一個消息,若是這個消息不爲null,則經過msg.target.dispatchMessage(msg)分發給相應的Handler進行處理。msg.target其實就是發出該消息的Handler對象。Handler在發出一個消息時會將自身存儲在Message內部的target變量中,以後在分析Handler時會講到。咱們先來看一下dispatchMessage方法的源碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); //經過handleCallback來執行Message對象內部的Runnable
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg); //將Message交給handleMessage方法進行處理
}
}
複製代碼
首先會檢查msg的callback對象是否爲null,這個callback是一個Runnable類型的對象,咱們知道Handler能夠發出兩種類型的消息,一種是經過sendMessage等方法直接發送一個Message消息對象,另外一種是經過則會經過post方法發送一個Runnable對象。若是發送的是一個Runnable對象,Handler在內部也會將這個Runnable對象封裝成一個Message對象,並將原來的Runnable對象賦值給Message的callback變量。若是msg.callback不爲null,說明該消息本來是經過Handler的post方法發出的一個Runnable,那麼會經過handleCallback方法直接執行這個Runnable。若是msg.callback對象爲null,那麼就將這個msg交給Handler的handleMessage方法進行處理。咱們在建立Handler對象時一般會重寫handleMessage方法來實現咱們想要的邏輯。
因爲loop方法內部實際上是一個無限循環,所以Looper對象會不斷的從MessageQueue對象中拿取消息並分發給對應的Handler進行處理。須要注意的是,若是咱們在子線程中調用了Looper.loop方法,那麼Looper中的無限循環會致使子線程阻塞,所以當咱們在子線程中使用了Looper後,應該在適當的時機調用looper對象的quit或quitSafely方法來退出這個Looper。
至此整個主線程的消息系統已經建立完成而且開始工做了,接下來咱們看一下Handler是如何將一個消息提交給相應的MessageQueue的。
Handler對象負責消息的發送和處理。咱們先來看一下Handler對象的構造方法:
public Handler(Callback callback, boolean async) {
...
mLooper = Looper.myLooper();//與Looper進行了綁定
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//與MessageQueue綁定
mCallback = callback;
mAsynchronous = async;
}
複製代碼
Handler對象有許多重載的構造方法,但這些構造方法最終都是調用的Hanler(Callback callback, boolean async)這個構造方法。在這個構造方法中,首先經過Looper.myLooper獲取到了一個Looper對象並賦值給了本身的一個成員變量,前面咱們說過,myLooper對象返回的是當前線程所獨有的Looper對象,這樣一來Handler,Looper和線程之間就一一對應起來了。所以,不管handler在哪一個線程發出消息,這個消息最終都會在handler初始化時所綁定的Looper所對應的線程中進行處理。
前面咱們說過,Handler能夠發出兩種類型的消息,一種是經過sendMessage方法發送一個Message對象,另外一種是經過post方法發送一個Runnable。咱們先來看一下sendMessage方法和post方法的源碼:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
複製代碼
能夠看到不管是post方法仍是sendMessage方法最終都是調用的sendMessageDelayed方法,不一樣的是在post方法中先調用了getPostMessage方法來對Runnable對象進行了一些處理,咱們來看一下getPostMessage方法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
複製代碼
正如咱們前面所說的,Handler將經過post方法提交的Runnable封裝在了一個Message對象內的callback變量裏。接下來咱們看一下sendMessageDelayed方法:
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
複製代碼
可見在sendMessageDelayed方法中又調用了sendMessageAtTime方法。而在sendMessageAtTime方法中,先拿取了初始化時綁定的MessageQueue對象,而後將這個MessageQueue和Message對象一塊兒傳給了enqueueMessage方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
複製代碼
在enqueueMessage方法中,Handler將自身賦值給了Message的target變量,前面在講loop方法的時候也說過,Message最終會經過這個target變量來獲取對應的Handler,所以,Message最終會被髮出該消息的Handler所處理。以後又調用了MessageQueue的enqueueMessage方法,最終將這個Message提交給了對應的MessageQueue對象。MessageQueue其實是一個單鏈表型的數據結構,鏈表中的前一個元素都會持有下一個元素的引用,而MessageQueue只須要持有第一個元素的引用便可。看一下MessageQueue的enqueueMessage方法:
boolean enqueueMessage(Message msg, long when) {
...
msg.when = when;
Message p = mMessages;//當前鏈表中的首個元素
boolean needWake;
if (p == null || when == 0 || when < p.when) {//若是鏈表中沒有元素或者要插入的message的執行時間早於隊列中的首個元素
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
...
}
return true;
}
複製代碼
當插入一個新的Message時,MessageQueue首先會判斷當前鏈表中是否存在元素,若是集合中的首個元素爲null,那麼就說明這個集合如今也是空的。若是集合中不存在元素,或新插入的Message不須要延時執行,或者要插入的Message的執行時間要早於集合中的首個元素的話,那麼直接將鏈表中的首個元素設置爲新插入的Message的下個元素,並將新插入的元素設置爲隊列中的首個元素。若是不知足上述條件的話,那麼會從頭開始遍歷集合,根據Message的執行時間來將Message插入到集合中的相應位置。可見MessageQueue雖然名字中帶有Queue,但並非一個標準的隊列,由於隊列只容許在表的後端插入元素。
至此,一個Message就成功被Handler提交到了Message中了。
因爲Android系統的消息機制比較複雜,本人技術水平很是有限,本文中不免會出現錯誤或者表達不許確的地方,但願你們可以幫忙指出,謝謝!
複製代碼