目錄
前言
Handler使用,請參見:Handler總結和補充_fdsafwagdagadg6576的專欄-CSDN博客
正文
Hander源碼機制包括四部分源碼:
Handler源碼,Looper源碼Message源碼,Messagequeue源碼.
分成java層和native層.
消息流程:
handler---messageQueue---Looper---hander,消息發送---消息接收---消息分發---消息處理.
類圖關系
1 Handler類
1.1 Handler 構造函數初始化
- 無參數構造函數
public class Handler {
/**我們通常用于創建Handler的構造方法之一*/
public Handler() {
this(null, false);
}
//===========step1: 構造函數====================
public Handler(Callback callback, boolean async) {
......
//=======step2://重點;獲取Looper(messagequeue管理者)=== /
mLooper = Looper.myLooper();
//=======step3: 獲取Looper的Messagequeue=====
mQueue = mLooper.mQueue;
//回調函數默認是Null
mCallback = callback;
//設置消息是否為異步處理方式
mAsynchronous = async;
}
根據調用關系:
step1: Handler構造函數中會去創建一個Looper對象。handler和Looper綁定,同時綁定Looper的messageQueue。
step2: Looper.myLooper()獲取Looper.
step3: mLooper.mQueue 獲取Looper的MessageQueue.
- 有參構造函數
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.
mainHandler = new Handler() 等價于 new Handler(Looper.myLooper())
Looper.myLooper():獲取當前進程的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);
}
// 最后調用此方法添加到消息隊列中
private boolean enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis) {
msg.target = this;// 設置發送目標對象是Handler本身
......
return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
}
step1: Message.obtain
step2:enqueueMessage(queue, msg, uptimeMillis)
step3: msg.target = this;// 設置發送目標對象是Handler本身
return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
1.3 消息處理
case1: handleMessage(msg);
是派生一個Hanlder子類并重寫其handleMessage方法來處理具體的消息。
發送發是sendMessage.
case 2:handleCallback & mCallback.handleMessage(msg)
用callback來創建一個Handler的實例而無需派生Handler的子類。而Callback給我們提供了另外一種方式,那就是當我們不想派生子類的時候,可以通過Callback來實現.
call ladder:
post
--sendMessageDelayed
----getPostMessage
------m.callback = r//Runnable對象
--------new Handler(callback)
Message的callback是什么?其實就是一個Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數
private static void handleCallback(Message message) {
message.callback.run();
}
發送方:
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()之后的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()后,loop()才會中止,其后的代碼才能得以運行.
1.4 異常處理
內存泄漏
//檢查Handler是否是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());
}
}
小結:handler類:1) init 綁定looper,queue; 2) send msg 3) handle msg .
handler模型是以消息為中心,將消息放入主線程,消息綁定了handler對象,進而調用了handler執行函數。實現了對象,消息和函數綁定.
handler.sendmessage(msg)--msg.target.handlemsg(msg).
改寫成以對象為中心的實現call ladder:Looper.enqueuemessage(handler)---handler.handlemessage(handler.msg).
2 Looper類
looper類:1) create queue 2) 循環讀取 3) 分發
looper是兩個線程,一個線程send message放入msg,一個線程epoll_wait 獲取消息.
Looper.loop()循環取Message中的消息,回調msg.target.dispatchMessage(msg)。
Handler sendMessage 調用enqueueMessage,enqueueMessage第一行msg.target = this;這個this是什么呢?這個this在handler方法中自然是handler本身了,所以msg.target.dispatchMessage(msg);可以找到正確的handler,消息分開不會出錯.
2.1 創建MessageQueue
//Looper類的構造方法創建了消息隊列MessageQueue對象
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mRun = true;
mThread = Thread.currentThread();
}
一個線程一個Looper一個MessageQueue.
2.2 獲取Looper對象
通過ThreadLocal獲取Looper對象
//handler調用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。
public static Looper myLooper() {
return sThreadLocal.get();
}
2.3 Looper 主循環
Looper 讀取message & 分發message
//looper中最重要的方法loop(),該方法是個死循環,會不斷去消息隊列MessageQueue中獲取消息,
//然后調dispatchMessage(msg)方法去執行
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
//進入loop的主循環方法
for (;;) {
Message msg = queue.next(); // might block
......
//msg.target is handler;分發消息
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
- Message msg= queue.next() ; 讀取MessageQueue的下一條Message;
- msg.target.dispatchMessage(msg);把Message分發給相應的target;
next源碼分析,參見MessageQueue類分析
下面是dispatchMessage分析
// 在looper類中的loop()方法內部調用的方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
分發給handleMessage 和handleCallback 處理.
2.4 子線程創建Looper(擴展)
如果子線程中使用Looper.prepare()和Looper.loop()創建了消息隊列,就可以讓消息處理在該線程中完成.
Looper.prepare() 通過map ThreadLocal綁定線程和新建的Looper(包含MessageQueue).判斷looper在哪個線程內運行。
Looper.loop()不斷地從當前線程對應Looper的MessageQueue中取出消息進行處理.
//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() 原因是非主線程中默認沒有創建Looper對象,需要先調用Looper.prepare()啟用Looper,然后再調用Looper.loop()。
子線程自己創建Looper
class childThread extends Thread{
public Handler mHandler;
@Override
public void run() {
//子線程中必須先創建Looper
Looper.prepare();
mHandler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//處理消息
}
};
//啟動looper循環
Looper.loop();
}
}
2.5 ThreadLocal
ThreadLocal機制:通常的全局變量,各個子線程是共享的。ThreadLocal變量特殊性是,它是global變量,但是每個子線程的ThreadLocal值是私有的.
原理是內部有個map管理每個線程和對應的變量.感興趣的同學可以在網上搜顯示源碼.
ThreadLocal和線程局部變量又什么區別?線程局部變量完全可以替代ThreadLocal,但是ThreadLocal使用簡單,java采用局部變量的方法比較少.兩者的關系錢存在自己家和銀行,實際是一樣. Threadlocal 可以使一個Looper類統一管理所有線程的looper.
ThreadLocal與局部變量 ThreadLocal與局部變量_Jack Cheung-CSDN博客
3 MessageQueue類
線程同步在messageQueue的native層實現.
消息的循環,發送和處理
1) 生產者線程和消費者線程同步:
如果messageQueue為空: messageQueued.next 通過epoll監聽messagequeue綁定的文件fd,實現讀寫同步.
如果messageQueue不為空: 生產者線程寫,消費者線程讀
nativePollOnce 阻塞,nativeWake 喚醒
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
//...
synchronized (this) {
boolean needWake;
Message p = mMessages;
//如果處于阻塞狀態,并且鏈表頭部是一個同步屏障,并且插入消息是最早的異步消息,需要喚醒
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//下面是一個鏈表的插入操作,將消息按時間順序插入到mMessages中
for (;;) {
prev = p;
p = p.next;
//如果在找到插入位置之前,發現了異步消息的存在,不需要喚醒
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p;
prev.next = msg;
}
//nativeWake方法會喚醒當前線程的MessageQueue
if (needWake) {
nativeWake(mPtr);
}
}
next() :提取下一條message.在這里messagequeue 阻塞. epoll 監聽與messagequeue綁定的文件fd,詳見:玩Android必看:從源碼角度看Handler - 嗶哩嗶哩
Message next() {
final long ptr = mPtr;
......
for (;;) {
//阻塞操作,當等待nextPollTimeoutMillis時長,或者消息隊列被喚醒,都會返回
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) 多個生產者線程同時寫:使用messageQueued.enqueueMessage中的鎖同步.
messageQueue 從java-jni-native c++流程,參見:Android消息機制2-Handler(Native層) - Gityuan博客 | 袁輝輝的技術博客
4 Message.java源碼:
讀寫Parcel數據
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資源進入休眠狀態,直到有下個消息進來時候就會喚醒主線程. 使用Linux pipe/epoll機制,通過往pipe管道寫端寫入數據來喚醒主線程工作。原理類似于I/O,讀寫是堵塞的,不占用CPU資源。 \ - handler中發送消息線程安全嗎?
線程安全。因為使用lock進行了線程同步.
MessageQueue中插入消息enqueueMessage方法以及取消息next都是線程安全的,都使用了synchronized進行加鎖.
小結:
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層) - 簡書