我們經常說的Handler機制是一套包含Handler,Looper,MessageQueue的異步消息處理系統,今天就分析一下這套機制的原理。
1.相關概念
1)Message
定義:消息,理解為線程間通訊的數據單元(Handler接受和處理的消息對象。)
2)Message Queue
定義:消息隊列
作用:用來存放通過Handler發過來的消息,按照先進先出執行
提供enqueueMessage 方法,將消息根據時間放置到隊列中;
提供next方法,從隊列中獲取消息,沒有消息的時候阻塞。
3)Handler
定義:Handler是Message的主要處理者
作用:負責將Message添加到消息隊列&處理Looper分派過來的Message
提供sendMessage方法,將消息放置到隊列中
提供handleMessage方法,定義個各種消息的處理方式;
4)Looper
定義:循環器,扮演Message Queue和Handler之間橋梁的角色
作用:主要負責消息循環:循環取出Message Queue的Message;消息派發:將取出的Message交付給相應的Handler。
Looper.prepare():實例化Looper對象;為當前線程生成一個消息隊列;
Looper.loop() :循環從消息隊列中獲取消息,交給Handler處理;此時線程處于無限循環中,不停的從MessageQueue中獲取Message 消息 ;如果沒有消息就阻塞。
幾個需要注意的地方:
1)每個線程中只能擁有一個Looper,但是一個Looper可以和多個線程的Handler綁定起來,也就是說很多個線程可以往一個Looper所持有的MessageQueue中發送消息。這就給我們提供了線程之間通信的可能。
2)Handler在創建的時候可以顯示指定Looper,這樣在Handler在調用sendMessage()投遞消息的時候會將消息添加到指定的Looper里面的MessageQueue。如果不指定Looper,Handler默認綁定的是創建它的線程的Looper。
下面是Handler機制運行圖:
工作流程圖解釋:
異步通信傳遞機制步驟主要包括異步通信的準備、消息發送、消息循環和消息處理
1)異步通信的準備
包括Looper對象的創建&實例化、MessageQueue隊列的創建和Handler的實例化。
2)消息發送
Handler將消息發送到消息隊列中
3)消息循環
Looper執行Looper.loop()進入消息循環,在這個循環過程中,不斷從該Message Queue取出消息,并將取出的消息派發給創建該消息的Handler
4)消息處理
調用該Handler的dispatchMessage(msg)方法,即回調handleMessage(msg)處理消息。
2.工作流程詳解
(1)Looper
Looper主要負責:
1)自身的創建&創建Message Queue
2)消息循環(消息取出、派發)
對應職責我們來看下相應的源碼:
1)自身的創建&創建Message Queue:prepare()方法
public static final void prepare() {
//判斷sThreadLocal是否為null,否則拋出異常
//即Looper.prepare()方法不能被調用兩次
//也就是說,一個線程中只能對應一個Looper實例
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//sThreadLocal是一個ThreadLocal對象,用于在一個線程中存儲變量
//實例化Looper對象并存放在ThreadLocal
//這說明Looper是存放在Thread線程里的
sThreadLocal.set(new Looper(true));
}
//再來看下Looper的構造方法
private Looper(boolean quitAllowed) {
//創建了一個MessageQueue(消息隊列)
//這說明,當創建一個Looper實例時,會自動創建一個與之配對的MessageQueue(消息隊列)
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
2)消息循環:loop()方法
```
public static void loop() {
//myLooper()方法作用是返回sThreadLocal存儲的Looper實例,如果me為null,loop()則拋出異常
//也就是說loop方法的執行必須在prepare方法之后運行
//也就是說,消息循環必須要先在線程當中創建Looper實例
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取looper實例中的mQueue(消息隊列)
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//進入消息循環
for (;;) {
//next()方法用于取出消息隊列里的消息
//如果取出的消息為空,則線程阻塞
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//消息派發:把消息派發給msg的target屬性,然后用dispatchMessage方法去處理
//Msg的target其實就是handler對象,下面會繼續分析
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
//釋放消息占據的資源
msg.recycle();
}
}
```
總結一下Looper的作用:
1)實例化本身、與當前線程綁定、創建與之相應的MessageQueue:prepare()方法,一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue。
2)消息循環(消息取出、消息派發):loop()方法
不斷從MessageQueue中去取消息,派發給消息的target屬性的Handler,然后調用相應Handler的dispatchMessage()方法進行消息處理。
(2)Handler
Handler主要負責:
1)在子線程發送消息給MessageQueue
2)處理Looper派發過來的消息
Handler是需要和線程綁定在一起的,在初始化Handler的時候一般通過指定Looper對象從而綁定相應線程,即給Handler指定Looper對象=綁定到了Looper對象所在的線程中,Handler的消息處理回調會在那個線程中執行。一般有兩種方法創建:
1)通過Loop.myLooper()得到當前線程的Looper對象/通過Loop.getMainLooper()可以獲得當前進程的主線程的Looper對象。
2)不指定Looper對象,那么這個Handler綁定到了創建這個線程的線程上,消息處理回調也就在創建線程中執行。
首先看一下Handler的構造方法
public Handler() {
this(null, false);
}
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());
}
}
//通過Looper.myLooper()獲取了當前線程保存的Looper實例,如果線程沒有Looper實例那么會拋出異常
//這說明在一個沒有創建Looper的線程中是無法創建一個Handler對象的
//所以說我們在子線程中創建一個Handler時首先需要創建Looper,并且開啟消息循環才能夠使用這個Handler。
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取了這個Looper實例中保存的MessageQueue(消息隊列)
//這樣就保證了handler的實例與我們Looper實例中MessageQueue關聯上了
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
上述說明:當Handler初始化時,可通過構造方法自動關聯Looper和相應的MessageQueue。
Handler發送消息有send和post兩種,下面分別看一下這兩種方法:
1)send的發送方法:sendMessage()
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
//我們往下扒
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
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
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//調用了enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
//調用sendMessage方法其實最后是調用了enqueueMessage方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//為msg.target賦值為this,也就是把當前的handler作為msg的target屬性
//如果大家還記得Looper的loop()方法會取出每個msg然后執行msg.target.dispatchMessage(msg)去處理消息,其實就是派發給相應的Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最終調用queue的enqueueMessage的方法,也就是說handler發出的消息,最終會保存到消息隊列中去。
return queue.enqueueMessage(msg, uptimeMillis);
}
2)Post的發送方法:sendMessage()
```
showhandler.post(new Runnable() {
@Override
public void run() {
String line = "\n";
StringBuffer text = new StringBuffer(show.getText());
text.append(line).append("angelababy:Yes,I do");
show.setText(text);
}
相比send方法,post方法最大的不同在于,更新UI操作可直接在重寫的run方法定義。
其實Runnable并沒有創建什么線程,而是發送了一條消息,下面看源碼:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
//創建了一個Message對象
//創建Message對象可以new,也可以使用Message.obtain()方法;
//但是更建議使用obtain方法,因為Message內部維護了一個Message池用于Message的復用,避免使用new 重新分配內存。
Message m = Message.obtain();
//將我們創建的Runable對象作為callback屬性,賦值給了此message.
m.callback = r;
//創建了一個Message對象
return m;
}
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);
}
從上面的源碼發現了吧?和send中的handler.sendMessage是一樣的。調用了sendMessageAtTime,然后調用了enqueueMessage方法,給msg.target賦值為handler,最終Handler將消息加入MessagQueue。
但是細心的你會發現,在使用Post方法時會將我們創建的Runable對象作為callback屬性賦值給了此message,那么msg的callback和target都有值,那么會執行哪個呢?我們已知回調發送消息的方法是:dispatchMessage()
public void dispatchMessage(Message msg) {
//一開始就會進行判斷
//如果msg.callback屬性不為null,則執行callback回調,也就是我們的Runnable對象
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
可以看到dispathMessage()方法里調用了 handleMessage()方法,但handleMessage()是一個空方法。
因為Handler發送消息過來是希望進行一定的處理,至于怎么處理消息是該Handler最終控制的,所以我們在創建handler時需要通過復寫handleMessage()方法從而實現我們需要的消息處理方式,然后根據msg.what標識進行消息處理。
需要注意的地方:
在一個Android應用啟動的時候,會創建一個主線程,即ActivityThread(也叫UI線程),在ActivityThread中有一個靜態的main方法:應用程序的入口點。
//一個進程會默認生成一個主線程
public static void main(String[] args) {
......
//主線程生成時自動通過prepareMainLooper方法為主線程創建一個Looper
//prepare()方法是用于在子線程中創建一個Looper對象,在子線程中是可以退出消息循環的:調用消息隊列的quit方法
//Looper生成時會自動生成與之配套的消息隊列
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
......
//loop()方法開啟消息循環
//主線程的消息循環是不允許被退出的
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
(3)MessageQueue
即消息隊列,用于存放Handler發送過來的消息,為了提高插入刪除的效率,采用單鏈表的方式實現。下面分別看一下MessageQueue的入隊和出隊操作。
1)MessageQueue入隊
boolean enqueueMessage(Message msg, long when) {
......
synchronized (this) {
......
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;
}
消息的入隊(插入)過程
首先判斷消息隊列里有沒有消息,沒有的話則將當前插入的消息作為隊頭,并且這時消息隊列如果處于等待狀態的話則將其喚醒。
若是在中間插入,則根據Message創建的時間進行插入。
2)MessageQueue出隊
Message next() {
......
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,這時候消息隊列處于等待狀態。
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 {
// 如果消息隊列中沒有消息,將nextPollTimeoutMillis設為-1,下次循環消息隊列則處于等待狀態
nextPollTimeoutMillis = -1;
}
//退出消息隊列,返回null,這時候Looper中的消息循環也會終止。
if (mQuitting) {
dispose();
return null;
}
......
}
.....
}
}
參考文章:http://www.lxweimin.com/p/9fe944ee02f7