Android消息機制1-Handler(Java層)(轉)
轉自:http://gityuan.com/2015/12/26/handler-message-framework/
相關源碼
framework/base/core/java/andorid/os/Handler.java
framework/base/core/java/andorid/os/Looper.java
framework/base/core/java/andorid/os/Message.java
framework/base/core/java/andorid/os/MessageQueue.java
libcore/luni/src/main/java/java/lang/ThreadLocal.java
一、概述
在整個Android的源碼世界裏,有兩大利劍,其一是Binder IPC機制,,另一個便是消息機制(由Handler/Looper/MessageQueue等構成的)。關於Binder在Binder系列中詳細講解過,有興趣看看。
Android有大量的消息驅動方式來進行交互,比如Android的四劍客Activity
, Service
, Broadcast
, ContentProvider
的啟動過程的交互,都離不開消息機制,Android某種意義上也可以說成是一個以消息驅動的系統。消息機制涉及MessageQueue/Message/Looper/Handler這4個類。
1.1 模型
消息機制主要包含:
- Message:消息分為硬件產生的消息(如按鈕、觸摸)和軟件生成的消息;
- MessageQueue:消息隊列的主要功能向消息池投遞消息(
MessageQueue.enqueueMessage
)和取走消息池的消息(MessageQueue.next
- Handler:消息輔助類,主要功能向消息池發送各種消息事件(
Handler.sendMessage
)和處理相應消息事件(Handler.handleMessage
); - Looper:不斷循環執行(
Looper.loop
),按分發機制將消息分發給目標處理者。
1.2 架構圖
- Looper有一個MessageQueue消息隊列;
- MessageQueue有一組待處理的Message;
- Message中有一個用於處理消息的Handler;
- Handler中有Looper和MessageQueue。
1.3 典型實例
先展示一個典型的關於Handler/Looper的線程
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //【見 2.1】
mHandler = new Handler() { //【見 3.1】
public void handleMessage(Message msg) {
//TODO 定義消息處理邏輯. 【見 3.2】
}
};
Looper.loop(); //【見 2.2】
}
}
接下來,圍繞著這個實例展開詳細分析。
二、Looper
2.1 prepare()
對於無參的情況,默認調用prepare(true)
,表示的是這個Looper運行退出,而對於false的情況則表示當前Looper不運行退出。
private static void prepare(boolean quitAllowed) {
//每個線程只允許執行一次該方法,第二次執行時線程的TLS已有數據,則會拋出異常。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//創建Looper對象,並保存到當前線程的TLS區域
sThreadLocal.set(new Looper(quitAllowed));
}
這裏的sThreadLocal
是ThreadLocal類型,下面,先說說ThreadLocal。
ThreadLocal: 線程本地存儲區(Thread Local Storage,簡稱為TLS),每個線程都有自己的私有的本地存儲區域,不同線程之間彼此不能訪問對方的TLS區域。TLS常用的操作方法:
-
ThreadLocal.set(T value)
:將value存儲到當前線程的TLS區域,源碼如下:public void set(T value) { Thread currentThread = Thread.currentThread(); //獲取當前線程 Values values = values(currentThread); //查找當前線程的本地儲存區 if (values == null) { //當線程本地存儲區,尚未存儲該線程相關信息時,則創建Values對象 values = initializeValues(currentThread); } //保存數據value到當前線程this values.put(this, value); }
-
ThreadLocal.get()
:獲取當前線程TLS區域的數據,源碼如下:public T get() { Thread currentThread = Thread.currentThread(); //獲取當前線程 Values values = values(currentThread); //查找當前線程的本地儲存區 if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; //返回當前線程儲存區中的數據 } } else { //創建Values對象 values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); //從目標線程存儲區沒有查詢是則返回null }
ThreadLocal的get()和set()方法操作的類型都是泛型,接著回到前面提到的sThreadLocal
變量,其定義如下:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>()
可見sThreadLocal
的get()和set()操作的類型都是Looper
類型。
Looper.prepare()
Looper.prepare()在每個線程只允許執行一次,該方法會創建Looper對象,Looper的構造方法中會創建一個MessageQueue對象,再將Looper對象保存到當前線程TLS。
對於Looper類型的構造方法如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //創建MessageQueue對象. 【見4.1】
mThread = Thread.currentThread(); //記錄當前線程.
}
另外,與prepare()相近功能的,還有一個prepareMainLooper()
方法,該方法主要在ActivityThread類中使用。
public static void prepareMainLooper() {
prepare(false); //設置不允許退出的Looper
synchronized (Looper.class) {
//將當前的Looper保存為主Looper,每個線程只允許執行一次。
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
2.2 loop()
public static void loop() {
final Looper me = myLooper(); //獲取TLS存儲的Looper對象 【見2.4】
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
}
final MessageQueue queue = me.mQueue; //獲取Looper對象中的消息隊列
Binder.clearCallingIdentity();
//確保在權限檢查時基於本地進程,而不是基於最初調用進程。
final long ident = Binder.clearCallingIdentity();
for (;;) { //進入loop的主循環方法
Message msg = queue.next(); //可能會阻塞 【見4.2】
if (msg == null) { //沒有消息,則退出循環
return;
}
Printer logging = me.mLogging; //默認為null,可通過setMessageLogging()方法來指定輸出,用於debug功能
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //用於分發Message 【見3.2】
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity(); //確保分發過程中identity不會損壞
if (ident != newIdent) {
//打印identity改變的log,在分發消息過程中是不希望身份被改變的。
}
msg.recycleUnchecked(); //將Message放入消息池 【見5.2】
}
}
loop()進入循環模式,不斷重復下面的操作,直到沒有消息時退出循環
- 讀取MessageQueue的下一條Message;
- 把Message分發給相應的target;
- 再把分發後的Message回收到消息池,以便重復利用。
這是這個消息處理的核心部分。另外,上面代碼中可以看到有logging方法,這是用於debug的,默認情況下logging == null
,通過設置setMessageLogging()用來開啟debug工作。
2.3 quit()
public void quit() {
mQueue.quit(false); //消息移除
}
public void quitSafely() {
mQueue.quit(true); //安全地消息移除
}
Looper.quit()方法的實現最終調用的是MessageQueue.quit()方法
MessageQueue.quit()
void quit(boolean safe) {
// 當mQuitAllowed為false,表示不運行退出,強行調用quit()會拋出異常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) { //防止多次執行退出操作
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked(); //移除尚未觸發的所有消息
} else {
removeAllMessagesLocked(); //移除所有的消息
}
//mQuitting=false,那麽認定為 mPtr != 0
nativeWake(mPtr);
}
}
消息退出的方式:
- 當safe =true時,只移除尚未觸發的所有消息,對於正在觸發的消息並不移除;
- 當safe =flase時,移除所有的消息
2.4 常用方法
2.4.1 myLooper
用於獲取TLS存儲的Looper對象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
2.4.2 post
發送消息,並設置消息的callback,用於處理消息。
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;
}
三、Handler
3.1 創建Handler
3.1.1 無參構造
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
//匿名類、內部類或本地類都必須申明為static,否則會警告可能出現內存泄露
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.prepare(),才能獲取Looper對象,否則為null.
mLooper = Looper.myLooper(); //從當前線程的TLS中獲取Looper對象【見2.1】
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //消息隊列,來自Looper對象
mCallback = callback; //回調方法
mAsynchronous = async; //設置消息是否為異步處理方式
}
對於Handler的無參構造方法,默認采用當前線程TLS中的Looper對象,並且callback回調方法為null,且消息為同步處理方式。只要執行的Looper.prepare()方法,那麽便可以獲取有效的Looper對象。
3.1.2 有參構造
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
Handler類在構造方法中,可指定Looper,Callback回調方法以及消息的處理方式(同步或異步),對於無參的handler,默認是當前線程的Looper。
3.2 消息分發機制
在Looper.loop()中,當發現有消息時,調用消息的目標handler,執行dispatchMessage()方法來分發消息。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//當Message存在回調方法,回調msg.callback.run()方法;
handleCallback(msg);
} else {
if (mCallback != null) {
//當Handler存在Callback成員變量時,回調方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回調方法handleMessage()
handleMessage(msg);
}
}
分發消息流程:
- 當
Message
的回調方法不為空時,則回調方法msg.callback.run()
,其中callBack數據類型為Runnable,否則進入步驟2; - 當
Handler
的mCallback
成員變量不為空時,則回調方法mCallback.handleMessage(msg)
,否則進入步驟3; - 調用
Handler
自身的回調方法handleMessage()
,該方法默認為空,Handler子類通過覆寫該方法來完成具體的邏輯。
對於很多情況下,消息分發後的處理方法是第3種情況,即Handler.handleMessage(),一般地往往通過覆寫該方法從而實現自己的業務邏輯。
3.3 消息發送
發送消息調用鏈:
從上圖,可以發現所有的發消息方式,最終都是調用MessageQueue.enqueueMessage()
;
3.3.1 sendEmptyMessage
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
3.3.2 sendEmptyMessageDelayed
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
3.3.3 sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
3.3.4 sendMessageAtTime
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
3.3.5 sendMessageAtFrontOfQueue
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
return false;
}
return enqueueMessage(queue, msg, 0);
}
該方法通過設置消息的觸發時間為0,從而使Message加入到消息隊列的隊頭。
3.3.6 post
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;
}
3.3.7 postAtFrontOfQueue
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
3.3.8 enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); 【見4.3】
}
3.3.8 小節
Handler.sendEmptyMessage()
等系列方法最終調用MessageQueue.enqueueMessage(msg, uptimeMillis)
,將消息添加到消息隊列中,其中uptimeMillis為系統當前的運行時間,不包括休眠時間。
3.4 其他方法
3.4.1 obtainMessage
獲取消息
public final Message obtainMessage()
{
return Message.obtain(this); 【見5.2】
}
Handler.obtainMessage()
方法,最終調用Message.obtainMessage(this)
,其中this為當前的Handler對象。
3.4.2 removeMessages
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null); 【見 4.5】
}
Handler
是消息機制中非常重要的輔助類,更多的實現都是MessageQueue
, Message
中的方法,Handler的目的是為了更加方便的使用消息機制。
四、MessageQueue
MessageQueue是消息機制的Java層和C++層的連接紐帶,大部分核心方法都交給native層來處理,其中MessageQueue類中涉及的native方法如下:
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
private native void nativePollOnce(long ptr, int timeoutMillis);
private native static void nativeWake(long ptr);
private native static boolean nativeIsPolling(long ptr);
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
關於這些native方法的介紹,見Android消息機制2-Handler(native篇)。
4.1 創建MessageQueue
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
//通過native方法初始化消息隊列,其中mPtr是供native代碼使用
mPtr = nativeInit();
}
4.2 next()
提取下一條message
Message next() {
final long ptr = mPtr;
if (ptr == 0) { //當消息循環已經退出,則直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 循環叠代的首次為-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//當消息Handler為空時,查詢MessageQueue中的下一條異步消息msg,則退出循環。
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 {
// 獲取一條消息,並返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
//設置消息的使用狀態,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地獲取MessageQueue中的下一條即將要執行的消息
}
} else {
//沒有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
//當消息隊列為空,或者是消息隊列的第一個消息時
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//沒有idle handlers 需要運行,則循環並等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//只有第一次循環時,會運行idle handlers,執行完成後,重置pendingIdleHandlerCount為0.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; //去掉handler的引用
boolean keep = false;
try {
keep = idler.queueIdle(); //idle時執行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置idle handler個數為0,以保證不會再次重復運行
pendingIdleHandlerCount = 0;
//當調用一個空閑handler時,一個新message能夠被分發,因此無需等待可以直接查詢pending message.
nextPollTimeoutMillis = 0;
}
}
nativePollOnce
是阻塞操作,其中nextPollTimeoutMillis
代表下一個消息到來前,還需要等待的時長;當nextPollTimeoutMillis = -1時,表示消息隊列中無消息,會一直等待下去。
當處於空閑時,往往會執行IdleHandler
中的方法。當nativePollOnce()返回後,next()從mMessages
中提取一個消息。
nativePollOnce()
在native做了大量的工作,想進一步了解可查看 Android消息機制2-Handler(native篇)。
4.3 enqueueMessage
添加一條消息到消息隊列
boolean enqueueMessage(Message msg, long when) {
// 每一個Message必須有一個target
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) { //正在退出時,回收msg,加入到消息池
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p為null(代表MessageQueue沒有消息) 或者msg的觸發時間是隊列中最早的, 則進入該該分支
msg.next = p;
mMessages = msg;
needWake = mBlocked; //當阻塞時需要喚醒
} else {
//將消息按時間順序插入到MessageQueue。一般地,不需要喚醒事件隊列,除非
//消息隊頭存在barrier,並且同時Message是隊列中最早的異步消息。
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;
prev.next = msg;
}
//消息沒有退出,我們認為此時mPtr != 0
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue
是按照Message觸發時間的先後順序排列的,隊頭的消息是將要最早觸發的消息。當有消息需要加入消息隊列時,會從隊列頭開始遍歷,直到找到消息應該插入的合適位置,以保證所有消息的時間順序。
4.4 removeMessages
void removeMessages(Handler h, int what, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
//從消息隊列的頭部開始,移除所有符合條件的消息
while (p != null && p.target == h && p.what == what
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
//移除剩余的符合要求的消息
while (p != null) {
Message n = p.next;
if (n != null) {
if (n.target == h && n.what == what
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
這個移除消息的方法,采用了兩個while循環,第一個循環是從隊頭開始,移除符合條件的消息,第二個循環是從頭部移除完連續的滿足條件的消息之後,再從隊列後面繼續查詢是否有滿足條件的消息需要被移除。
五、 Message
5.1 創建消息
每個消息用Message
表示,Message
主要包含以下內容:
數據類型 | 成員變量 | 解釋 |
---|---|---|
int | what | 消息類別 |
long | when | 消息觸發時間 |
int | arg1 | 參數1 |
int | arg2 | 參數2 |
Object | obj | 消息內容 |
Handler | target | 消息響應方 |
Runnable | callback | 回調方法 |
創建消息的過程,就是填充消息的上述內容的一項或多項。
5.2 消息池
在代碼中,可能經常看到recycle()方法,咋一看,可能是在做虛擬機的gc()相關的工作,其實不然,這是用於把消息加入到消息池的作用。這樣的好處是,當消息池不為空時,可以直接從消息池中獲取Message對象,而不是直接創建,提高效率。
靜態變量sPool
的數據類型為Message,通過next成員變量,維護一個消息池;靜態變量MAX_POOL_SIZE
代表消息池的可用大小;消息池的默認大小為50。
消息池常用的操作方法是obtain()和recycle()。
5.2.1 obtain
從消息池中獲取消息
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null; //從sPool中取出一個Message對象,並消息鏈表斷開
m.flags = 0; // 清除in-use flag
sPoolSize--; //消息池的可用大小進行減1操作
return m;
}
}
return new Message(); // 當消息池為空時,直接創建Message對象
}
obtain(),從消息池取Message,都是把消息池表頭的Message取走,再把表頭指向next;
5.2.2 recycle
把不再使用的消息加入消息池
public void recycle() {
if (isInUse()) { //判斷消息是否正在使用
if (gCheckRecycle) { //Android 5.0以後的版本默認為true,之前的版本默認為false.
throw new IllegalStateException("This message cannot be recycled because it is still in use.");
}
return;
}
recycleUnchecked();
}
//對於不再使用的消息,加入到消息池
void recycleUnchecked() {
//將消息標示位置為IN_USE,並清空消息所有的參數。
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) { //當消息池沒有滿時,將Message對象加入消息池
next = sPool;
sPool = this;
sPoolSize++; //消息池的可用大小進行加1操作
}
}
}
recycle(),將Message加入到消息池的過程,都是把Message加到鏈表的表頭;
六、總結
最後用一張圖,來表示整個消息機制
圖解:
- Handler通過sendMessage()發送Message到MessageQueue隊列;
- Looper通過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
- 經過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
- 將Message加入MessageQueue時,處往管道寫入字符,可以會喚醒loop線程;如果MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler接口中的方法,往往用於做一些清理性地工作。
消息分發的優先級:
- Message的回調方法:
message.callback.run()
,優先級最高; - Handler的回調方法:
Handler.mCallback.handleMessage(msg)
,優先級僅次於1; - Handler的默認方法:
Handler.handleMessage(msg)
,優先級最低。
Android消息機制1-Handler(Java層)(轉)