android 消息系統Handler、MessageQueue、Looper源代碼學習
android消息系統
總體框架如圖所看到的
在安卓的消息系統中,每一個線程有一個Looper,Looper中有一個MessageQueue,Handler向這個隊列中投遞Message,Looper循環拿出Message再交由Handler處理。總體是一個生產者消費者模式,這四部分也就構成了android的消息系統。
先來看一個最簡單的樣例
//這段代碼在某個Activity的onCreate中
Handler handler = new Handler (Looper.getMainLooper());
Message msg = Message.obtain(handler, new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext,"I am a message",Toast.LENGTH_SHORT).show();
}
});
handler.sendMessage(msg);
效果就是。在當前窗體彈出I am a message。當然就事實上現的效果而言全然多此一舉。可是就分析android消息系統。卻是非常easy有效的樣例。
源代碼分析
Message
Message中封裝了我們經常使用的what、arg1、arg2、obj等參數,除此之外還有target:一個Handler類型,由前文可知一個Message終於還是交給一個Handler運行的。這個target存放的就是消息的目的地、callback。一個消息的回調。我們通過handler.post(new Runnable{…})發送的消息。這個Runnable即被存為callback。
首先來看消息的獲取:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
對比最開始的樣例,Message.obtain(Handler h, Runnable callback)首先調用obtain獲取了一個新的Message對象。然後為其設置了目的地Handler和回調函數callback。Message類中有非常多不同的obtain函數。實際上僅僅是為我們封裝了一些賦值的操作。
再看Message.obtain()方法。sPoolSync是一個給靜態方法用的靜態鎖。sPool是一個靜態的Message變量。在消息的獲取這裏,android使用了享元模式,對於會被反復使用的Message消息。沒有對每一次請求都新建一個對象。而是通過維護一個Message鏈表,在有空暇消息的時候從鏈表中拿Message。沒有時才新建Message。
能夠看到obtain中僅僅有從鏈表中去Message和新建Message。而沒有向鏈表中存儲的過程。
存儲這部分就要看Message.recycle()了:
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
回收過程。首先把原鏈表的頭指向當前被回收消息的下一個節點。然後再把鏈表頭指針知道當前節點就可以。整個操作也就是將Message加入到鏈表的首位。
MessageQueue 消息隊列
MessageQueue是在Looper中的。這點從Looper的構造函數能夠看出來:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
對於每一個MessageQueue,是鏈表實現的消息隊列。
首先是入隊操作:
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
synchronized (this) {
if (mQuitting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
//mMessages是鏈表的頭指針
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 將消息插入到隊列的首位
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; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
next操作。包括取出和刪除一條消息。
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//從native層消息隊列取出消息
nativePollOnce(mPtr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// 找到非異步的Message或者消息隊列尾部的Message取出
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 {
// 獲取一個Message
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// 檢查退出標誌位
if (mQuitting) {
dispose();
return null;
}
...
}
}
Handler
Handler的作用是放入消息和處理消息,承擔了生產者的工作和部分消費者的工作。
首先通過Handler發送一條消息:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
通過一層一層嵌套,真正的邏輯在sendMessageAtTime,能夠看到僅僅是運行了一下入隊操作。作為生產者的工作也就運行完畢,消費者部分後面要結合Looper分析。
除了sendMessage方法,經常使用的handler.post方法也是封裝為Message,主要過程和上面類似。
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;
}
Looper
Looper類中,Looper的實例獲取是通過ThreadLocal的。ThreadLocal會為每一個線程提供一個副本,通過set和get方法每一個線程獲取作用域僅屬於該線程的變量值。對於UI線程而言,會運行Looper.prepareMainLooper()來完畢Looper的初始化:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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()方法將當前線程的ThreadLocal設置了一個新的Looper對象。prepareMainLooper則是把當前線程的Looper對象賦值給類變量sMainLooper 。該方法在ActivityThread中調用,設置了一個全局的給UI線程使用的Looper。
Looper的loop方法就是消費者的處理邏輯了:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
}
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//從Looper中獲取MessageQueue,循環取出消息
for (;;) {
Message msg = queue.next();
...
//將消息發送給目標處理。
msg.target.dispatchMessage(msg);
...
//回收消息,把消息放在消息池中
msg.recycle();
}
}
主要邏輯非常清晰,前面分析過msg.target是一個Handler,表示處理消息的目標,通過命令模式將消息交給相應Handler處理。
以下是Handler中處理消息的方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
public void handleMessage(Message msg) {
}
假設我們是通過handler.post的方法發送一條消息,那麽直接運行callback中的邏輯。
否則通過實現Callback接口回調,或者運行handleMessage,handleMessage也就是我們子類覆寫的方法。
能夠看到,盡管邏輯部分是我們在Handler中實現的。可是調用的地方卻是Looper的線程。由於一個Looper綁定一個線程,我們也能夠通過比較Looper來比較線程。
總結
通過分析源代碼,能夠知道android中能夠通過Looper為每一個線程創建一個消息隊裏,UI線程的Looper在Activity啟動前就已經初始化。
那麽對於我們自己定義的線程。非常明顯也能夠綁定Looper。
自己定義線程綁定Looper。最明顯的優點就是能夠實現線程間通信了,同一時候由於借助了消息隊列,也將並行轉為串行實現了線程安全。看一個簡單的樣例:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handlerA = new Handler(Looper.myLooper()){
@Override
public void handleMessage(Message msg) {
Log.d("TAG", msg.obj.toString());
}
};
Looper.loop();
}
}).start();
上述在線程中創建綁定了一個Looper,然後新建一個和當前Looper綁定的Handler,這樣能夠通過該Handler向Looper的MessageQueue中加入消息,然後由Looper.loop取出消息並運行。
Message msg = new Message();
msg.obj = "i am main thread";
handlerA.sendMessage(msg);
在主線程或者其他線程中獲取handler然後發送消息,終於能夠看到消息被線程接收並處理。
這裏msg的target也就是handlerA。
註意假設線程工作結束,須要調用Looper.quit()。不然會由於Looper一直循環而導致線程無法結束。
最後經過上面的分析,流程圖能夠畫的更為仔細:
android 消息系統Handler、MessageQueue、Looper源代碼學習