??在學習Android過程中,Android的消息機制是必須需要掌握的。樓主也只能算是一個Android的初學者,對Android的消息機制掌握的也不算很好。這里記錄一下我自己對Android消息機制的理解,錯誤之處,希望各位指正。
??本文參考文章:
??1.Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系
??2.Android應用程序消息處理機制(Looper、Handler)分析
1.概述
??要講消息處理機制,不得不講的就是Handler、Looper、MessageQueue這個重量級的人物。在這里對他們做一個概述,待會會詳細的介紹他們。
??從大體上來說,Handler的主要作用就是發送Message和處理Message;Looper的作用就是不斷從MessageQueue(消息隊列)中取Message,然后交給Handler處理;MessageQueue存放由Handler發送的Message。通常來說,Message里面就是需要主線程執行的操作,比如,我們從網絡獲取的文字內容信息,需要顯示到TextView上面去。
2.Handler
??首先我們從上面走下去,從Handler開始走。
??通常來說,使用Handler來處理異步消息,有基本的步驟。這里做一個簡單的例子,在線程中發送一條String,讓TextView來顯示。
private Button mButton = null;
//創建一個Handler的對象,注意我們重寫了handleMessage消息
//handleMessage方法就是用來處理我們發送的消息
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
mButton.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button: {
sendMessage();
break;
}
}
}
private void sendMessage() {
new Thread(new Runnable() {
@Override
public void run() {
//創建一個Message的對象
Message message = Message.obtain();
message.obj = "我被點擊了";
//發送消息
mHandler.sendMessage(message);
}
}).start();
}
??上面代碼中,我相信不難吧。現在我們需要理解的是Handler是怎么將一個Message從子線程傳遞到主線程中,然后執行的!
(1).sendMessage方法
??我們需要理解其中的緣由,我們來sendMessage方法的代碼。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
??我們在一步一步的跟蹤下去,最終到了enqueueMessage方法中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
??其中,這個方法的三個參數,queue表示message需要進入的消息隊列,msg就是我們之前創建的Message對象,uptimeMillis表示延遲時間。延遲時間這里不重要,我們先不看。
??我們從源代碼中可以看出,首先msg.target = this。這句話將一個handler賦值到message的target屬性,這一步非常重要,后面需要用的。這里我們先記住,這里的意思就是,表示當前的這個message屬于哪個handler,因為有可能有很多的handler都在發送消息!
?? mAsynchronous我們不看,這里不重要。最后是調用了queue.enqueueMessage(msg, uptimeMillis)方法,從這個方法的字面意思中,我們就可以看出來,這個方法是將一個message添加到queue的消息隊列。
??我們先不看queue里面的代碼。我們先來總結我們得到的信息。
??1.我們在子線程中發送一個Message,最終會進入到一個queue的消息隊列中去。
??2.在Message進入消息隊列之前,使用target字段來標記了當前的Message是哪個Handler發送的。
??到這里,我們好像沒有收獲,不急,我們來看看在構造一個Handler的時候,給我們創建那些東西!
(2).Handler的構造方法
??Handler的構造方法最終調用到了Handler(Callback callback, boolean async)方法里面。
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
??在Handler的構造方法中,我們發現了幾個小細節,mLooper、mQueue都被賦值了。Looper我們待會講,我們在調用sendMessage方法的時候,發現其中調用到這一步:
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);
}
??我們注意到的是,之前的enqueueMessage里面的queue就是mQueue。
??到這里,我們知道了:
??1.Handler在創建的時候,會創建一個MessageQueue和一個Looper,也就是說,一個Handler同時綁定一個MessageQueue和一個Looper。
??2.我們在調用sendMessage等方法來發送消息的時候,消息進入的消息隊列就是發送消息的Handler自己的MessageQueue。
2.Looper
??記得在概述中描述,Handler是發送消息和處理消息,現在我們對發送消息有了一個簡單的認識,但是我們對處理消息沒有介紹。不急,要想知道處理消息,我們必須先知道,消息是怎么被獲取出來的,難道是Handler直接伸手從MessageQueue中去拿嗎?怎么可能這么簡單嗎?接下來,我們介紹一下Looper,又一位重量級的人物
(1).Looper構造方法
??我們先來看看Looper的構造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
??我們在這個構造方法里面發現,Looper創建一個MessageQueue。但是除了這個信息,似乎沒看其他有用的信息了。
??怎么辦?通常來說使用Looper不會直接的創建,從這里我們也看到它的構造方法是private的,而是調用prepare方法。
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方法中,需要注意的是:sThreadLocal變量是一個ThreadLocal變量。這種類型的變量有什么呢?具體的解釋大家可以到網上去找(其實我也不是很懂,哈哈!!!)。這里簡單的解釋一下,ThreadLocal里面封裝了一個變量,用來存儲相應的信息,關鍵是不同的線程從ThreadLocal里面獲取的變量是不同的,每個線程從ThreadLocal獲取的是自己的變量,線程之間的是不會相互影響。
??從這里,我們可以得到是,每個線程都會有一個自己的Looper對象。
??到這里,我們知道:
??1.Looper的對象不能直接new,而是調用靜態方法prepare方法來創建
??2.同一個線程不能調用兩次prepare方法,因為Looper對象保存在一個ThreadLocal對象。
??3.從之前的Handler中,我們可以看到Handler里面持有了一個Looper對象(通過調用方法mLooper = Looper.myLooper())。這個對象就是TreadLocal封裝的對象。從而得出,如果這個Handler在主線程中創建的,那么Looper肯定在屬于主線程。
??從上面的結論中,我們可以得出,如果我們在一個線程中創建一個Handler,必須先創建當前線程的Looper對象。但是我們,你會發現,之前那個例子中,我們直接在一個Activity中創建一個Handler對象,而沒有創建主線程的Looper,這個為什么沒有報錯呢?我們可以假設,我們在創建Handler時,主線程的Looper早已經被prepare了!但是具體在哪里調用的呢?待會我們會展示!
(2).loop方法
??在之前說過,Looper的作用就是不斷從MessageQueue(消息隊列)中取Message,然后交給Handler處理。但是我們到現在還不知道Looper到底是怎么從MessageQueue里面去消息的,這個就得引出我們的loop方法。
??我們先來看看loop方法的源代碼,沒有貼出完整的代碼,而是刪除了一些自己認為不重要的代碼:
public static void loop() {
//取得當前線程的Looper--還記得我們之前的說的ThreadLocal變量,這個變量里面保存的就是當前線程的
//Looper對象
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;
// 死循環
for (;;) {
//如果沒有消息的話,當前線程會被阻塞住,
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//將當前的Message分發到Message自己的Handler
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
??我們先來整理一下,這個方法主要描述內容:
??1.首先獲取當前線程的Looper對象和MessageQueue對象
??2.從MessageQueue里面去獲取Message;如果獲取到Message,則分發到Message自己的Handler去處理;反之,沒有獲取到信息,線程會被阻塞在next方法里面。
??3.通過Handler的dispatchMessage方法將獲取的到Message從MessageQueue傳遞到了Handler,讓Handler去處理。
??到這里,我們來整理一下思路。首先,在主線程創建了一個Handler,表示當前的Looper是放在主線程中的,當然MessageQueue也是屬于主線程的。這里需要說明的是,就是一個線程只能有一個Looper,由于MessageQueue在Looper里面,所以MessageQueue也是只有一個,但是一個線程中Handler可以會被多次創建,所以,屬于同一個線程的Handler中持有的是同一個MessageQueue和Looper。
??我們使用在主線程中創建的Handler通過調用sendMessage等方法來發送消息,最終進入的消息隊列是主線程的MessageQueue。我們在Looper的loop方法中獲取Message,然后將Message分發到Message的target,也就是發送Message的Handler,讓它來處理。
??整個消息傳遞的過程就是這樣的,這個就能解釋,為什么我們在子線程中發送一個Message,在主線程的Handler能接收得到,從而進行對消息處理。因為Handler本身就是屬于主線程,包括Handler里面持有的Looper和MessageQueue都是屬于主線程。
??這里有一個疑問,那就是,我們知道我們在主線程中創建Handler時,其實主線程的Looper早就已經創建好了,這一點是怎么看出來得呢?
??例如:
new Thread(new Runnable() {
@Override
public void run() {
//這里將Looper的prepare方法注釋了
//表示當前的線程Looper沒有被初始化
// Looper.prepare();
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i("pby123", msg.obj.toString() + " " + Thread.currentThread().getName());
}
};
Message message = Message.obtain();
message.obj = "pby123";
mHandler.sendMessage(message);
}
}).start();
??然后我們來運行一下程序,會發現一個臭名昭著的異常!
??這個異常我們應該不陌生,因為凡是要使用Looper的地方,都用check一下Looper是否preapre完成。
??上面的樣例,我們是從一個新的線程里面創建Handler,由于是新的線程,就必須將這個線程的Looper和MessageQueue創建好。
??從而得出,我們在主線程中可以直接創建Handler,表示主線程的Looper和MessageQueue在我們創建Handler之前就已經創建完畢了!
??那到底是在哪里創建主線程的Looper。這個得引出ActivityThread類的main方法,我們都知道main方法是Java程序的入口,我們來看看main方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
??是不是看到main方法就感覺非常的親切,終于可以回到當初學習Java的時候了。我也是,看到main方法之后,感覺之前有好多的問題都解決了!
??在main方法里面,我們看到:Looper.prepareMainLooper();這一句話的意思就是初始化的主線程的Looper。從這里,我們知道了,為什么我們在主線程里面可以創建Handler,而不用去prepare Looper。
??同時,我們還注意的是,Looper.loop()。我們之前已經分析過了,loop方法主要是在MessageQueue里面去取消息來處理。由于這里是主線程的Looper,所以這里的loop方法是從主線程的MessageQueue里面取Message。
??之前,我們使用一個在主線程創建的Handler在一個子線程發送Message,最后發送到了主線程的MessageQueue。但是什么時候調用的loop呢?其實就是在main方法里面調用了的。
??同樣的道理,如果我們在一個新的線程里面分別創建Handler、Looper、MessageQueue,當調用Handler的sendMessage方法來發送一條Message,在Handler的handleMessage方法是不會接收到這個Message對象,這個是由于Looper的loop方法啟動,當前線程
??我們知道,loop方法是一個死循環,但是為什么不會導致應用ANR呢?(Application Not Response)。其實網絡上有很多的解釋,可能是自己太笨了,理解不了那些思路。下面將來結合Looper的loop方法和MessageQueue的部分方法來解釋一下原因。
4.MessageQueue
??我們知道MessageQueue相當于是一個存放Message的容器,Handler往這個容器里面放入Message,Looper從這個容器取數據。我們先來看看存放數據的部分
??在Looper的loop方法里面有這么一句:
Message msg = queue.next(); // might block
??上面這一句,就是Looper從MessageQueue里面取Message的操作,我們注意一下官方的注釋,這一句可能會導致阻塞!
??那么什么情況下會被阻塞呢?是不是當前MessageQueue為空的時候,會被阻塞呢?如果MessageQueue為空時,會被阻塞,這一句又是什么作用呢?
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
(1).next方法
??我們先來看看next方法,這里我先將next的所有代碼貼出,然后一部分一部分的分析
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 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(TAG, "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;
}
}
??首先,我們來看看這一句話
nativePollOnce(ptr, nextPollTimeoutMillis);
??根據Android應用程序消息處理機制(Looper、Handler)分析中介紹,nativePollOnce方法是用來表示當前線程會被阻塞,nextPollTimeoutMillis表達的意思就是當前的線程需需要阻塞多久。其中如果nextPollTimeoutMillis為0,表示不阻塞;-1表示無限期的阻塞,除非有線程喚醒它;其他的正數值表示阻塞多久。
??然后我們來看看下面的代碼
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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;
}
??其中,msg表示當前的消息隊列中第一個可以用的Message。其中先判斷了當前的時間與消息執行的時間的大小,如果表示當前的時間小于Message執行的時間,也就是說,還沒有到達該Message執行的時間,因此給nextPollTimeoutMillis賦值,表示線程要阻塞多久。
??但是,如果當前的時間符合Message的執行時間了,就得馬上返回這個Message,去執行里面的操作。
??如果當前取得的第一個Message是空的,表示當前的MessageQueue里面沒有了Message了,所以將nextPollTimeoutMills重置為-1,表示當前將無限期的阻塞下去,除非其他的線程將喚醒!
??其中我們還需要注意的是:
if (mQuitting) {
dispose();
return null;
}
??這個return是整個next方法里面第二個return null,還記得我們在loop方法里面的一段代碼嗎?就是如果從next方法里面獲取的方法是null的話,那么直接退出了loop循環。這個return null的返回條件就是就能解釋我們之前的那個疑問--如果MessageQueue為空時,會被阻塞,這一句又是什么作用呢?
??第一個return null的條件是mPtr等于0時,關于mPtr等于0是表示什么情況,我也不是清楚,大概應該說,當前的MessageQueue對頭的地址吧,這個只是我的猜測不一定正確!
??還是來看看第二個return null的條件吧,從中可以知道只有當mQuiting為true,才能返回 null。從這個變量名,我們可以知道,當這個MessageQueue正在quit時,會返回null。也就是說,當MessageQueue要被回收時,Looper會退出它的loop死循環。其他的情況下,loop都是在進行死循環的。
??但是,在哪一種情況下MessageQueue會被回收呢?我們發現MessageQueue的quit方法是默認修飾符,也就是說,只能在相同的包下,這個方法才能被調用!在我們的應用程序代碼中是不能直接調用的,那哪里可以調用了quit方法呢?我們在Looper的quit方法里面找到了答案:
public void quit() {
mQueue.quit(false);
}
??我們知道,每一個Looper對象是被放在了一個ThreadLocal變量中,也就是說,在哪個線程調用Looper的quit方法,就是quit哪個線程的MessageQueue,從而退出這個Looper的loop循環。
??例如:
private void sendMessage() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i("pby123", (msg.obj) + "");
}
};
Message message = Message.obtain();
message.obj = "我是Message";
handler.sendMessageDelayed(message, 2000);
Looper.loop();
Log.i("pby123", "123");
}
}).start();
}
??上面的代碼中,我們在一個新的線程里面創建了一個Handler、Looper,然后使用Handler發送了一個消息,最后將調用Looper的loop方法,讓這個Looper活起來!然后我們來看看log:
??我們會發現,123這個log始終沒有打印出來,也就是說,根本沒有執行到這一句話,從而推出此時Looper的loop方法還在執行,當然如果沒有消息的話,應該是被阻塞住了。這個現象間接的證明了,我們對源碼的理解!
??這里需要注意的是,在同一個線程中,代碼不能這樣寫:
new Thread(new Runnable() {
@Override
public void run() {
//這里將Looper的prepare方法注釋了
//表示當前的線程Looper沒有被初始化
Looper.prepare();
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("pby123", (msg.obj) + "");
}
};
Looper.loop();
Message message = Message.obtain();
message.obj = "我是Message";
mHandler.sendMessageDelayed(message, 2000);
Log.i("pby123", "123");
}
}).start();
??這樣寫的話,loop一直在執行,根本不能執行到loop的代碼!
??從而得知:
??1.除主線程之外,所有線程的Looper的loop方法不會自動調用,盡管我們調用Handler發送消息的相關方法。只有當我們在線程中明確調用loop方法,才會進行loop的循環。
??2.loop方法是一個死循環,它不斷的從MessageQueue里面去拿Message。只有當取出的Message為null時,loop的死循環才能被結束。
??2.在MessageQueue的next方法里面,如果取出的Message的執行時間大于當前的時間的話,當前線程就會被阻塞相應的差值時間。但是如果當前的MessageQueue為空的話,那么就會當前的線程就會被無限期的阻塞。
??3.如果當前的MessageQueue被調用了quit方法,Looper的loop方法從MessageQueue里面取出的是null,從而導致Looper的loop方法執行完成。
??4.MessageQueue的quit方法不能被直接調用,而是通過Handler的quit來間接實現的!
??到這里,我們可以知道,為什么loop方法不會導致ANR。這是因為loop不是一直執行,而是當前MessageQueue里面有Message時,才會不斷的去獲取Message;如果MessageQueue是空的話,線程會被阻塞,只有當消息的時候才會被喚醒,loop方法才算是又活起來了!
(2).enqueueMessage方法
??說實話,next方法真的不是很好的理解,特別是結合了Handler、Looper之后!但是,我們最終還是對next有了一個大概的理解,我可不敢說深入的理解,哈哈!因為自己本來就是一個菜雞!
??對next方法有了一個大概的理解,那么理解enqueueMessage方法應該不是很難的!先來看看enqueueMessage方法的代碼:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
??上面的代碼中,我刪除了部分我認為不重要的代碼。這個方法非常好理解,首先判斷這個MessageQueue是否被標記為回收。如果被回收了的話,返回false,表示插入新的Message失敗;其次如果沒有被回收,那么插入將Message插入到正確的位置,這個正確的位置表示的是,按照Message的執行時間從小到大排序,時間最小的,放在隊頭!
??我覺得這里最重要的一步是這個:
if (needWake) {
nativeWake(mPtr);
}
??如果當前需要喚醒的話,就去喚醒!
5.總結
??說實話,現在這篇文章寫得差不多了,該寫一個總結,將理解到的知識概括一下!
??1.在創建Handler之前,必須將當前線程的Looper線程創建完畢,也就是Looper的preapre方法必須放在Handler創建之前。之所以在線程中不用創建Looper,那是因為Google爸爸在ActivityThread的main方法里面已經給我們創建好了!
??2.Looper不會自動調用,必須在本線程中的最后一行代碼來調用!因為一旦loop方法執行完畢,表示當前的線程即將執行完畢。這個可以參考ActivityThread的main方法代碼!
??3.在調用Handler的發送Message的相關方法時,最終會將該Message放入與該線程綁定的MessageQueue里面。在放入Message,需要注意的是,Message本身帶了when屬性,這個表示Message時間,MessageQueue內部使用的這個When來排的序。
??4.在Looper的loop方法中,會不斷的調用MessageQueue的next來獲取一個Message。在獲取Message時,要分為兩種情況:1.Message不為null,那么就該Message分發到Message屬于的那個Handler里面去消耗;2.如果為null的話,表示當前的MessageQueue已經被調用quit方法了,loop方法就會結束,整個線程就會結束!
??5.在MessageQueue的next方法里面,如果獲取的Message執行時間大于當前的時間,表示還沒有達到該Message的執行時機,于是讓當前的線程阻塞相應的時間;如果獲取的Message符合執行時機的話,那么立即返回;如果當前的MessageQueue為空了,立即阻塞當前的線程!
??最后,在結合的知識來解釋一下,為什么我們在一個子線程中發送一個Message,主線程會被接受得到。
??首先,我們我們使用的是在主線程中創建Handler來發送Message,雖然說是在子線程發送Message,但是Message有一個target用來記錄發送它的Handler。同時用于Handler在創建的時候,會持有當前線程的Looper和MessageQueue,所以這個Message最終還是發送了主線程的MessageQueue中。而在ActivityThread的main方法里面,主線程的Looper在不斷的loop,所以不斷的從自己線程的MessageQueue取Message來消耗,Message被獲取之后,然后交給了target的handlerMessage方法去消耗,這個target就是在主線程我們自己創建的Handler。最終,我們就能知道為什么在子線程里面發送的Message能夠到達主線程中的Handler