|
下圖為Android消息處理機(jī)制順序圖:
由圖可知,第一步通過調(diào)用Looper.prepare()來創(chuàng)建Looper和MessageQueue java對象,MessageQueue java類通過保存單向鏈表頭mMessages來遍歷所有消息,注意此單向鏈表按時(shí)間順序從早到晚排列,因此mMessages指向的消息總是為需最早處理的消息。在第5步創(chuàng)建C++ Looper對象,在其中創(chuàng)建讀寫管道并通過epoll來監(jiān)控讀管道。 第7步創(chuàng)建Handler給用戶使用,第8步在消息處理線程內(nèi)調(diào)用Looper.loop()來進(jìn)入消息處理循環(huán),在10步取下一消息時(shí)調(diào)用nativePollOnce方法,直到13步調(diào)到Looper.pollOnce(),在這里將等待管道POLLIN事件一定時(shí)間(timeoutMillis毫秒),如果有事件則返回否則等待timeoutMillis毫秒。在第10步MessageQueue.next()取下一消息時(shí),將無限循環(huán)直到得到消息。循環(huán)時(shí)第一次傳的超時(shí)時(shí)間timeoutMillis=0,如果此時(shí)尚無消息,則檢查是否有IdleHandler,如有則調(diào)用所有Handler并置Handler為空,timeoutMillis設(shè)為0進(jìn)行第二次查詢, 以后timeoutMillis將設(shè)為-1,因此線程將一直掛起,直到有消息到來。
public class MessageQueue{
final Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0; //第一次立刻返回不等待
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(mPtr, nextPollTimeoutMillis); //第一次=0
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
final Message msg = mMessages;
if (msg != null) {
final long when = msg.when;
if (now >= when) { //如果消息發(fā)送時(shí)間滿足
mBlocked = false;
mMessages = msg.next;
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
msg.markInUse();
return msg; //取消息成功
} else {
nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); //如果有消息但消息尚未到達(dá)發(fā)送時(shí)間,則以該時(shí)間到當(dāng)前時(shí)間的時(shí)間差作為下次查詢超時(shí)的時(shí)間
}
} else {
nextPollTimeoutMillis = -1; //無限等待
}
// If first time, then get the number of idlers to run.
if (pendingIdleHandlerCount < 0) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount == 0) { //如果尚未設(shè)置IdleHandler
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
第17步用戶線程通過Handler發(fā)送消息,在20步將調(diào)用nativeWake來喚醒消息處理線程。最終第24步消息被處理。
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg
+ " This message is already in use.");
}
if (msg.target == null && !mQuitAllowed) {
throw new RuntimeException("Main thread not allowed to quit");
}
final boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
return false;
} else if (msg.target == null) {
mQuiting = true;
}
msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) { //如果消息隊(duì)列為空或者當(dāng)前消息為發(fā)送時(shí)間最早的消息
msg.next = p;
mMessages = msg;
needWake = mBlocked; // mBlocked=true表示線程已被掛起
} else {
Message prev = null;
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
msg.next = prev.next;
prev.next = msg;
needWake = false; // still waiting on head, no need to wake up
}
}
if (needWake) {
nativeWake(mPtr); //喚醒線程
}
return true;
}MessageQueue java類單向鏈表:
public final class Message implements Parcelable {
Message next; //消息鏈表頭
典型的消息處理代碼
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare(); //創(chuàng)建Looper對象,在Looper對象創(chuàng)建時(shí)將創(chuàng)建MessageQueue.最后將Looper對象存儲在線程局部變量中
*
* mHandler = new Handler() { //創(chuàng)建Handler對象,在此將讀取線程局部變量得到本線程的Looper對象及消息隊(duì)列
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop(); //進(jìn)入Looper對象的消息處理循環(huán)
* }
* }
用戶主要操作接口類Handler,可以通過重載handleMessage()方法實(shí)現(xiàn)自定義方法處理: Handler.java
public void dispatchMessage(Message msg) {
if (msg.callback != null) { //如果消息已設(shè)置callback,則調(diào)用該callback函數(shù)
handleCallback(msg);
} else {
if (mCallback != null) { //如果Handler已設(shè)置callback,則調(diào)用該callback處理消息
if (mCallback.handleMessage(msg)) { //如果消息被處理
return;
}
}
handleMessage(msg); //默認(rèn)為空函數(shù),用戶可重載處理自定義消息
}
}
private final void handleCallback(Message message) {
message.callback.run();
}
public Handler(Callback callback) {
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;
mCallback = callback;
}用戶主要操作接口類Looper frameworks/base/core/java/android/os/Looper.java
public class Looper{
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
public static void loop() {
Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
// No target is a magic identifier for the quit message.
return;
}
long wallStart = 0;
long threadStart = 0;
msg.target.dispatchMessage(msg); //處理消息
}
msg.recycle();
}
}
}
問題: 1.為了實(shí)現(xiàn)消息隊(duì)列等待機(jī)制,采用epoll方式來實(shí)現(xiàn)來消息時(shí)消息隊(duì)列處理線程的喚醒和當(dāng)無消息時(shí)線程掛起。實(shí)際上如果采用java對象wait/notify來實(shí)現(xiàn)此種功能更為簡單,難道還有別的因素導(dǎo)致Google采用目前的設(shè)計(jì)? 分析:估計(jì)與IdleHandler有關(guān),目前當(dāng)消息隊(duì)列首次為空或取完所有消息后,如果注冊了IdleHandler則會調(diào)用這些Handler處理,然后才會無限等待下一個(gè)消息到來 2.跨進(jìn)程消息傳送如何實(shí)現(xiàn):如鼠標(biāo)鍵盤事件傳遞 |
|
|