目錄
前言
Handler使用,請參見:Handler總結(jié)和補充_fdsafwagdagadg6576的專欄-CSDN博客
正文
Hander源碼機制包括四部分源碼:
Handler源碼,Looper源碼Message源碼,Messagequeue源碼.
分成java層和native層.
消息流程:
handler---messageQueue---Looper---hander,消息發(fā)送---消息接收---消息分發(fā)---消息處理.
類圖關(guān)系

1 Handler類
1.1 Handler 構(gòu)造函數(shù)初始化
- 無參數(shù)構(gòu)造函數(shù)
public class Handler {
/**我們通常用于創(chuàng)建Handler的構(gòu)造方法之一*/
public Handler() {
this(null, false);
}
//===========step1: 構(gòu)造函數(shù)====================
public Handler(Callback callback, boolean async) {
......
//=======step2://重點;獲取Looper(messagequeue管理者)=== /
mLooper = Looper.myLooper();
//=======step3: 獲取Looper的Messagequeue=====
mQueue = mLooper.mQueue;
//回調(diào)函數(shù)默認(rèn)是Null
mCallback = callback;
//設(shè)置消息是否為異步處理方式
mAsynchronous = async;
}
根據(jù)調(diào)用關(guān)系:
step1: Handler構(gòu)造函數(shù)中會去創(chuàng)建一個Looper對象。handler和Looper綁定,同時綁定Looper的messageQueue。
step2: Looper.myLooper()獲取Looper.
step3: mLooper.mQueue 獲取Looper的MessageQueue.
- 有參構(gòu)造函數(shù)
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類在構(gòu)造方法中,可指定Looper,Callback回調(diào)方法以及消息的處理方式(同步或異步),對于無參的handler,默認(rèn)是當(dāng)前線程的Looper.
mainHandler = new Handler() 等價于 new Handler(Looper.myLooper())
Looper.myLooper():獲取當(dāng)前進(jìn)程的looper對象。詳見下文.
1.2 Handler send message
call laddder:
sendEmptyMessage
--sendEmptyMessageDelayed
----sendMessageDelayed
------sendMessageAtTime
--------enqueueMessage
/////////////////////////////////////////////////////////////////////////
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
......
return enqueueMessage(queue, msg, uptimeMillis);
}
// 最后調(diào)用此方法添加到消息隊列中
private boolean enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis) {
msg.target = this;// 設(shè)置發(fā)送目標(biāo)對象是Handler本身
......
return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
}
step1: Message.obtain
step2:enqueueMessage(queue, msg, uptimeMillis)
step3: msg.target = this;// 設(shè)置發(fā)送目標(biāo)對象是Handler本身
return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
1.3 消息處理
case1: handleMessage(msg);
是派生一個Hanlder子類并重寫其handleMessage方法來處理具體的消息。
發(fā)送發(fā)是sendMessage.
case 2:handleCallback & mCallback.handleMessage(msg)
用callback來創(chuàng)建一個Handler的實例而無需派生Handler的子類。而Callback給我們提供了另外一種方式,那就是當(dāng)我們不想派生子類的時候,可以通過Callback來實現(xiàn).
call ladder:
post
--sendMessageDelayed
----getPostMessage
------m.callback = r//Runnable對象
--------new Handler(callback)
Message的callback是什么?其實就是一個Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數(shù)
private static void handleCallback(Message message) {
message.callback.run();
}
發(fā)送方:
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;
}
最后要注意的是寫在Looper.loop()之后的代碼不會被執(zhí)行,這個函數(shù)內(nèi)部應(yīng)該是一個循環(huán),當(dāng)調(diào)用mHandler.getLooper().quit()后,loop()才會中止,其后的代碼才能得以運行.
1.4 異常處理
內(nèi)存泄漏
//檢查Handler是否是static的;如果不是的,那么有可能導(dǎo)致內(nèi)存泄露
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());
}
}
小結(jié):handler類:1) init 綁定looper,queue; 2) send msg 3) handle msg .
handler模型是以消息為中心,將消息放入主線程,消息綁定了handler對象,進(jìn)而調(diào)用了handler執(zhí)行函數(shù)。實現(xiàn)了對象,消息和函數(shù)綁定.
handler.sendmessage(msg)--msg.target.handlemsg(msg).
改寫成以對象為中心的實現(xiàn)call ladder:Looper.enqueuemessage(handler)---handler.handlemessage(handler.msg).
2 Looper類
looper類:1) create queue 2) 循環(huán)讀取 3) 分發(fā)
looper是兩個線程,一個線程send message放入msg,一個線程epoll_wait 獲取消息.
Looper.loop()循環(huán)取Message中的消息,回調(diào)msg.target.dispatchMessage(msg)。
Handler sendMessage 調(diào)用enqueueMessage,enqueueMessage第一行msg.target = this;這個this是什么呢?這個this在handler方法中自然是handler本身了,所以msg.target.dispatchMessage(msg);可以找到正確的handler,消息分開不會出錯.
2.1 創(chuàng)建MessageQueue
//Looper類的構(gòu)造方法創(chuàng)建了消息隊列MessageQueue對象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
一個線程一個Looper一個MessageQueue.
2.2 獲取Looper對象
通過ThreadLocal獲取Looper對象
//handler調(diào)用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。
public static Looper myLooper() {
return sThreadLocal.get();
}
2.3 Looper 主循環(huán)
Looper 讀取message & 分發(fā)message
//looper中最重要的方法loop(),該方法是個死循環(huán),會不斷去消息隊列MessageQueue中獲取消息,
//然后調(diào)dispatchMessage(msg)方法去執(zhí)行
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
//進(jìn)入loop的主循環(huán)方法
for (;;) {
Message msg = queue.next(); // might block
......
//msg.target is handler;分發(fā)消息
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
- Message msg= queue.next() ; 讀取MessageQueue的下一條Message;
- msg.target.dispatchMessage(msg);把Message分發(fā)給相應(yīng)的target;
next源碼分析,參見MessageQueue類分析
下面是dispatchMessage分析
// 在looper類中的loop()方法內(nèi)部調(diào)用的方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
分發(fā)給handleMessage 和handleCallback 處理.
2.4 子線程創(chuàng)建Looper(擴(kuò)展)
如果子線程中使用Looper.prepare()和Looper.loop()創(chuàng)建了消息隊列,就可以讓消息處理在該線程中完成.
Looper.prepare() 通過map ThreadLocal綁定線程和新建的Looper(包含MessageQueue).判斷l(xiāng)ooper在哪個線程內(nèi)運行。
Looper.loop()不斷地從當(dāng)前線程對應(yīng)Looper的MessageQueue中取出消息進(jìn)行處理.
//perpare()方法,用來初始化一個Looper對象
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
......
sThreadLocal.set(new Looper(quitAllowed));
}
在非主線程中直接new Handler() 會報如下的錯誤: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線程中默認(rèn)沒有創(chuàng)建Looper對象,需要先調(diào)用Looper.prepare()啟用Looper,然后再調(diào)用Looper.loop()。
子線程自己創(chuàng)建Looper
class childThread extends Thread{
public Handler mHandler;
@Override
public void run() {
//子線程中必須先創(chuàng)建Looper
Looper.prepare();
mHandler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//處理消息
}
};
//啟動looper循環(huán)
Looper.loop();
}
}
2.5 ThreadLocal
ThreadLocal機制:通常的全局變量,各個子線程是共享的。ThreadLocal變量特殊性是,它是global變量,但是每個子線程的ThreadLocal值是私有的.
原理是內(nèi)部有個map管理每個線程和對應(yīng)的變量.感興趣的同學(xué)可以在網(wǎng)上搜顯示源碼.
ThreadLocal和線程局部變量又什么區(qū)別?線程局部變量完全可以替代ThreadLocal,但是ThreadLocal使用簡單,java采用局部變量的方法比較少.兩者的關(guān)系錢存在自己家和銀行,實際是一樣. Threadlocal 可以使一個Looper類統(tǒng)一管理所有線程的looper.
ThreadLocal與局部變量 ThreadLocal與局部變量_Jack Cheung-CSDN博客
3 MessageQueue類
線程同步在messageQueue的native層實現(xiàn).
消息的循環(huán),發(fā)送和處理
1) 生產(chǎn)者線程和消費者線程同步:
如果messageQueue為空: messageQueued.next 通過epoll監(jiān)聽messagequeue綁定的文件fd,實現(xiàn)讀寫同步.
如果messageQueue不為空: 生產(chǎn)者線程寫,消費者線程讀
nativePollOnce 阻塞,nativeWake 喚醒
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//...
synchronized (this) {
boolean needWake;
Message p = mMessages;
//如果處于阻塞狀態(tài),并且鏈表頭部是一個同步屏障,并且插入消息是最早的異步消息,需要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//下面是一個鏈表的插入操作,將消息按時間順序插入到mMessages中
for (;;) {
prev = p;
p = p.next;
//如果在找到插入位置之前,發(fā)現(xiàn)了異步消息的存在,不需要喚醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
//nativeWake方法會喚醒當(dāng)前線程的MessageQueue
if (needWake) {
nativeWake(mPtr);
}
}
next() :提取下一條message.在這里messagequeue 阻塞. epoll 監(jiān)聽與messagequeue綁定的文件fd,詳見:玩Android必看:從源碼角度看Handler - 嗶哩嗶哩
Message next() {
final long ptr = mPtr;
......
for (;;) {
//阻塞操作,當(dāng)?shù)却齨extPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
......
}
}
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jint ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(timeoutMillis);
}
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
result = pollInner(timeoutMillis);
}
int Looper::pollInner(int timeoutMillis) {
......
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
}
}
void Looper::awoken() {
......
char buffer[16];
ssize_t nRead;
do {
nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer));
} while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer));
}
2) 多個生產(chǎn)者線程同時寫:使用messageQueued.enqueueMessage中的鎖同步.
messageQueue 從java-jni-native c++流程,參見:Android消息機制2-Handler(Native層) - Gityuan博客 | 袁輝輝的技術(shù)博客
4 Message.java源碼:
讀寫Parcel數(shù)據(jù)
private void readFromParcel(Parcel source) {
what = source.readInt();
arg1 = source.readInt();
arg2 = source.readInt();
if (source.readInt() != 0) {
obj = source.readParcelable(getClass().getClassLoader());
}
when = source.readLong();
data = source.readBundle();
replyTo = Messenger.readMessengerOrNullFromParcel(source);
sendingUid = source.readInt();
}
public void writeToParcel(Parcel dest, int flags)
{
Parcelable p = (Parcelable)obj;
dest.writeInt(1);
dest.writeParcelable(p, flags);
......
}
QA:
- 主線程中的looper是一直輪詢嗎?
不是。如果MessageQueue沒有消息時,就會阻塞在loop的queue.next()方法里. 這時候主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個消息進(jìn)來時候就會喚醒主線程. 使用Linux pipe/epoll機制,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。 \ - handler中發(fā)送消息線程安全嗎?
線程安全。因為使用lock進(jìn)行了線程同步.
MessageQueue中插入消息enqueueMessage方法以及取消息next都是線程安全的,都使用了synchronized進(jìn)行加鎖.
小結(jié):
Handler源碼:主要是綁定looper,sendmessage,handlemessage.
Looper源碼: 主要是prepare-->Loop { queue.next;msg.target.dispatchMessage(msg); -->sThreadLocal.set(new Looper)}
Messagequeue.java源碼:
Message next() {
nativePollOnce//epoll wait
}
enqueueMessage {
nativeWake //epoll wake
}
Message.java源碼
readFromParcel,writeToParcel
附錄源碼:Handler.java, Looper.java , Message.java,MessageQueue.java
類似文章: Android的消息機制(java層) Android的消息機制(java層) - 簡書
Android消息機制(native層) Android消息機制(native層) - 簡書