上一篇Handler消息機制(一)爲大家講述了Handler是如何創建的這篇Handler消息機制(二)將爲大家介紹Handler是如何發送消息的。
一般使用的Handler方法
Handler.post(Runnable)
Handler.sendEmptyMessage()
Handler.sendMessage()
Handler.sendMessageDelayed()
這四種方法是大家在App中經常使用的那麼我們就首先看一下後面三種sendMessage()的方法
第一,Hander.sendEmptyMessage()
看一下源碼
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
那麼sendEmptyMessageDelayed(what, 0)
的源碼是什麼呢?接着往下看
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
那麼調用的是sendMessageDelayed(msg, delayMillis)
這個方法,OK,第二種常調用的方法和第四種其實是一樣的只不過進行了一個封裝。
那麼我們再來第三種調用的方法的背後
Handler.sendMessage()方法的源碼
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
OK,大家其實已經明白後面三種發送消息的方法最後調用的都是sendMessageDelayed(msg, 0).這個方法。那讓我們來看看這個方法到底是何方神聖,請看一下這個方法的源碼。
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
最後調用的是sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
這個方法。這個方法有兩個參數,一個是需要發送的消息。另一個是發送消息的時間,這個時間是系統當前的時間加上設置的延遲時間。明白了,那麼我們來看看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);
}
在這個方法中首先涉及了一個類MessageQueue這個類是做什麼的,看類的名字就知道這是一個消息隊列。大家這個時候發現了一個對象mQueue,這個時候很多人就會好奇了這個mQueue對象是做什麼的?
從哪裏來,這個時候我們就要回到Handler消息機制(一)裏面實例化對象的時候實例化每個Handler必須要有Loop,那這個Loop到底起到了什麼作用?
我們重新看一下Handler()實例化對象時的構造方法
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;
}
大家這個時候有沒有發現 mQueue = mLooper.mQueue,這個時候眼前一亮,mQueue這個參數是用Loop獲得的,那麼我們就來看看mLooper.mQueue到底是個什麼鬼?
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
大家這個時候突然發現mQueue是在創建Looper對象時就會創建一個與Looper相關聯的消息隊列,那問題就來了Looper對象又是在什麼時候創建的。這個時候我們不得不聯繫到上一篇博客Hander消息機制(一),在Hander實例化對象時,讓我們來回顧一下當時的源代碼,實例化對象的時候如果不是在主線程中必須使用Loop.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));
}
一切清楚了,在Loop.prepare()這個方法的時候就實例化了一個Looper對象,然後在實例化Looper對象的時候又實例化了一個與這個Looper相關聯的MessageQueue(消息隊列),來理一下這個邏輯,意思就是每一個Looper都有一個與之相關的消息隊列,而Looper又是與什麼相關呢。在前面的ThreadLocal中介紹的就明白了,每一個線程對應一個與之相關的Looper。我們已經弄清楚了這個mQueue,現在重新回到發送消息時的方法
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, msg, uptimeMillis)
這個方法那我們來看看這個方法的源代碼是什麼?
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這個方法裏唯一要注意的就是 msg.target = this,這個表明其實msg.target其實就是當前Handler的實例化對象,當然這裏也許用不到,到下面怎麼接收這個消息將用到,大家發現最後調用的是queue.enqueueMessage(msg, uptimeMillis)這個方法,來看看這個方法的源代碼
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
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;
}
這裏大家需要理解清楚一個邏輯,就是消息進入消息隊列是怎樣進入的,大家可以仔細注意一下這段代碼, 大家從20行代碼往下看。這邊的邏輯首先是msg.when = when;Message p = mMessages這兩句
,when自然就是傳進來的參數,那麼mMessages是啥,大家其實這就是個消息的全局變量,連實例化這個對象也沒,那麼如果是第一次必然是p == null ,走入 if (p == null || when == 0 || when < p.when)代碼中,那這個時候會做什麼動作,msg.next = p;mMessages = msg;會把下一個消息變成p,mMessages可以理解爲當前傳進來的消息。這個時候如果再進來一個消息,是進行怎樣的判斷 if (p == null || when == 0 || when < p.when)這個裏面還有兩個參數,第一是when ==0, 第二是when< p.when ,when其實就是消息當前消息的when,但是這個時候的p已經變成上一次傳進來的消息,Message p = mMessages, 其實這個判斷就是說如果穿進來的消息的when爲0,或者when小於新進來的消息的when.就把這個上一個消息放到下一個消息。OK,理一下這個邏輯,如果msg.when越小就越在消息隊列的前面,when就是傳進來的系統時間和設置的延遲時間之和。
OK,我們開始講述最後一個方法Handler.post(Runnable),這裏我們首先要將一個預備知識點。很多安卓程序有這個誤區,就是實現一個Runnable接口到底是不是一個線程,那我們來做一個試驗,請看試驗代碼。
public class Test {
public static void main(String[] args) {
Test03 test03 = new Test03();
test03.run();
Thread thread = Thread.currentThread();
System.out.println("TestName =" + thread.getId() );
}
}
public class Test03 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
Thread thread = Thread.currentThread();
System.out.println("Test03Id =" +thread.getId());
}
}
打印結果:
Test03Id =1
TestName =1
這個打印結果的線程Id是一樣的,那麼這就證明一個問題,一個類實現一個Runable接口並不是說這個類變成單獨的一個線程,實際上只是實現了一個接口。那麼接下來接着來看Handler.post(Runnable)這個的源碼
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
這個最終調用的是sendMessageDelayed()方法,然後sendMessageDelayed()方法最終又是調用的sendMessageAtTime(),這是說通過
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
這段代碼其實就是告訴我們把Runnable接口變成一個消息塞了進去,然後給這個消息一個callback。OK,所有發消息的基本已經講述完畢。至於這三種方法怎麼回調,請看下一篇博客Handler消息機制源碼解析(三)。