轉載請註明出處:http://blog.csdn.net/vnanyesheshou/article/details/73484527
本文已授權微信公衆號 fanfan程序媛 獨家發佈 掃一掃文章底部的二維碼或在微信搜索 fanfan程序媛 即可關注
上一篇總結了一下Handler的基本用法,但是對於其原理並不太清楚,這篇主要分析下其內部的原理。看一下其源碼是怎麼回事,從源碼的角度理解Handler機制,Handler、Looper、MessageQueue之間的關係。
1 簡介 |
先對這幾個類做一下簡單介紹。
Handler:
線程間通信的方式,主要用來發送消息及處理消息。
Looper:
爲線程運行消息循環的類,循環取出MessageQueue中的Message;消息派發,將取出的Message交付給相應的Handler。
MessageQueue:
存放通過Handler發過來的消息,遵循先進先出原則。
Message:消息,線程間通信通訊攜帶的數據。
例如後臺線程在處理數據完畢後需要更新UI,則可發送一條包含更新信息的Message給UI線程
2 Looper |
源碼路徑:frameworks/base/core/java/android/os/Looper.java
Looper主要工作:
- 自身實例的創建,創建消息隊列,保證一個線程中最多有一個Looper實例。
- 消息循環,從消息隊列中取出消息,進行派發。
Looper用於爲線程運行消息循環的類,默認線程沒有與它們相關聯的消息循環;如果要想在子線程中進行消息循環,則需要在線程中調用prepare(),創建Looper對象。然後通過loop()方法來循環讀取消息進行派發,直到循環結束。
程序中使用Looper的地方:
- 主線程(UI線程)
UI線程中Looper已經都創建好了,不用我們去創建和循環。 - 普通線程
普通線程中使用Looper需要我們自己去prepare()、loop()。
看一下普通線程中創建使用Looper的方式,代碼如下:
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();
}
}
這段代碼是Looper源碼註釋中給的典型列子,主要步驟:
- Looper 準備,(Looper實例創建);
- 創建發送消息、處理消息的Handler對象;
- Looper開始運行。
印象中在UI線程沒有出現過Looper相關的東東,這是因爲UI線程中會自動創建Looper對象並進行消息循環,我們不再需要調用Looper.prepare()和Looper.loop(),但是在子線程中如果想要創建使用Handelr則需要向如上所示。
我們通過源碼看一下Looper實例創建的方法:
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));
}
Looper構造方法是私有的,只能通過prepare()進行創建Looper對象。prepare()會調用私有方法prepare(boolean quitAllowed)。
第6行 sThreadLocal爲ThreadLocal類型變量,用來存儲線程中的Looper對象。
prepare方法中首先判斷sThreadLocal是否存儲對象,如果存儲了則拋出異常,這是因爲在同一個線程中Loop.prepare()方法不能調用兩次,也就是同一個線程中最多有一個Looper實例(當然也可以沒有,如果子線程不需要創建Handler時)。
該異常應該許多朋友都遇見過,如在UI線程中調用Looper.prepare(),系統會替UI線程創建Looper實例,所以不需要再次調用prepare()。
接着看Looper的構造器:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
在構造器中,創建了一個MessageQueue消息隊列;然後獲取當前的線程,使Looper實例與線程綁定。
由prepare方法可知一個線程只會有一個Looper實例,所以一個Looper實例也只有一個MessageQueue實例。但這並不代表一個線程只能有一個MessageQueue實例,這是爲什麼呢?很簡單,我們可以自己new 一個MessageQueue實例就可以了,但這個MessageQueue並不是該線程中Handelr對應的消息隊列。
接着看Looper的消息循環:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//通過Looper實例獲取消息隊列
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //消息循環
//從消息隊列中取出一條消息,如果沒有消息則會阻塞。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//將消息派發給target屬性對應的handler,調用其dispatchMessage進行處理。
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
//log
}
msg.recycle();
}
}
loop()函數是靜態的,所以它只能訪問靜態數據。
第2行myLooper()函數也是靜態的,其代碼如下
return sThreadLocal.get()
獲取sThreadLocal存儲的Looper實例,如果爲空則拋出異常,這也說明loop()方法必須在prepare()方法之後才能調用。
第7行 通過Looper對象獲取消息隊列。
然後進行消息循環,從隊列中獲取消息,把消息交給msg的target的dispatchMessage方法進行處理,也就是交給handler進行處理,這個稍後說Handler時再細說。
然後調用msg.recycle(); 釋放msg。
Looper.loop()是給死循環,那如何終止消息循環呢?我們可以調用Looper的quit方法或quitSafely方法。
quit方法作用是把MessageQueue消息池中所有的消息全部清空,無論是延遲消息還是非延遲消息。
quitSafely只會清空MessageQueue消息隊列中所有的延遲消息,並將消息池中所有的非延遲消息派發出去讓Handler去處理,quitSafely相比於quit方法安全之處在於清空消息之前會派發所有的非延遲消息。
總結:
- UI線程會自動創建Looper實例、並且調用loop()方法,不需要我們再調用prepare()和loop().
- Looper與創建它的線程綁定,確保一個線程最多有一個Looper實例,同時一個Looper實例只有一個MessageQueue實例。
- loop()函數循環從MessageQueue中獲取消息,並將消息交給消息的target的dispatchMessage去處理。如果MessageQueue中沒有消息則獲取消息可能會阻塞。
- 通過調用Looper的quit或quitsafely終止消息循環。
3 Handler |
源碼路徑:frameworks/base/core/java/android/os/Handler.java
Handler主要職責:
- 發送消息給MessageQueue(消息隊列);
- 處理Looper派送過來的消息;
我們使用Handler一般都要初始化一個Handler實例。看下Handler的構造函數:
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//。。。。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
第8行 Looper.myLooper();獲取當前線程保存的Looper實例,如果當前線程沒有Looper實例則會拋出異常。這也就是說在線程中應該先創建Looper實例(通過Looper.prepare()),然後纔可以創建Handler實例。
第13行 獲取Looper實例所保存的MessageQueue。之後使用Handler sendMesage、post都會將消息發送到該消息隊列中。保證handler實例與該線程中唯一的Looper對象、及該Looper對象中的MessageQueue對象聯繫到一塊。
3.1sendMessage
接着看一下平常使用Handler發送消息,先看sendMessage的流程:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
sendMessage最終調用到enqueueMessage函數,接着看下enqueueMessage。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
在enqueueMessage中,首先設置msg的target屬性,值爲this。之前在Looper的loop方法中,從消息隊列中取出的msg,然後調用msg.target.dispatchMessage(msg);其實也就是調用當前handler的dispatchMessage函數。
然後調用queue的dispatchMessage方法,將Handler發出的消息,保存到消息隊列中。
3.2 post
看一下post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
post方法中調用getPostMessage方法,創建一個Message對象,設置此Message對象的callback屬性爲創建Runnable對象。
然後調用sendMessageDelayed,最終和sendMessage一樣,都是調用到sendMessageAtTime。調用enqueueMessage方法,將此msg添加到MessageQueue中。
也就是post(Runnable r) 並沒有創建線程。其run方法是在Handler對應的線程中運行的。
3.3 dispatchMessage
這裏主要說下handler是如何處理消息的。在Looper.loop方法中通過獲取到的msg,然後調用msg.target.dispatchMessage(msg);也就是調用handler的dispatchMessage方法,看下Handler中dispatchMessage源碼
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在dispatchMessage方法中首先判斷msg的callback屬性,如果不爲空則調用handleCallback函數,
handleCallback函數如下:
private static void handleCallback(Message message) {
message.callback.run();
}
handleCallback函數中messag.callback也就是我們傳的Runnable對象,也就是調用Runnable對象的run方法。
如果msg.callback屬性爲空,判斷Handler屬性mCallback是否爲空, 不爲空則讓mCallback處理該msg。
mCallback爲空則調用Handler的handleMessage,這就是我們創建Handler對象時一般都實現其handleMessage方法的原因。
4 MessageQueue |
源碼路徑:frameworks/base/core/java/android/os/MessageQueue.java
MessageQueue 消息隊列:
- enqueueMessage將消息加入隊列
- next從隊列取出消息
- removeMessage移除消息
MessageQueue內部是如何管理這些消息隊列的就先不說了,之後又空再好好分析一下。
5 總結 |
本文分析了下Handler、Looper、MessageQueue之間的聯繫,及handler進程間通信的原理。瞭解到Handler不僅僅可以更新UI,也可以在一個子線程中去創建一個Handler,然後使用這個handler實例在任何其他線程中發送消息,最終處理這些消息的代碼都會在你創建Handler實例的線程中運行。
歡迎掃一掃關注我的微信公衆號,定期推送優質技術文章: