1. Handler的使用
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Message msg = mHandler.obtainMessage(what);
mHandler.sendMessage(msg);
從上述代碼可知抡砂,我們使用Handler
的流程:
創(chuàng)建Handler -> 獲取消息對象 -> 發(fā)送消息 -> 處理消息
2. Handler的創(chuàng)建
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
構建函數中比較重要的是looper(Looper)
對象,mQueue(MessageQueue)
對象是與looper
相關聯(lián)的肿嘲,mCallback
及mAsynchronous
后續(xù)會講解偶芍。
3. Looper的來歷
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構建函數中設置當前線程對象到mThread變量中贞铣,并創(chuàng)建了一個MessageQueue
對象寥茫,其創(chuàng)建過程我們稍后分析。心細的讀者可能已經發(fā)現(xiàn)這是個私有構造函數边锁,那么肯定是在類內部進行調用的姑食,搜索發(fā)現(xiàn)其調用函數為:
public static void prepare() {
prepare(true);
}
// 目前quitAllowed應該僅在主線程中被設置成false,自定義線程中均為true
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));
}
Looper#prepare
方法會創(chuàng)建Looper
對象茅坛,并且某一線程僅能創(chuàng)建一個Looper
對象音半,然后將創(chuàng)建的對象設置為線程安全變量。那么這個函數在哪里調用咧贡蓖?既然跟線程相關曹鸠,那么大膽猜測是在線程中調用的,而Android中最主要的線程是主線程摩梧,我們一開始的“Handler
的基本使用”中并沒有創(chuàng)建Looper
物延,卻可以使用Handler
,那么Android主線程中必然在生成的時候就新建了一個Looper
仅父,查看Looper
源碼時會發(fā)現(xiàn)的確含有一個特殊的Looper
(即:sMainLooper
),這個特殊Looper
對象通過調用Looper#prepareMainLooper()
創(chuàng)建的浑吟,我們可以去ActivityThread
中去看是如何實現(xiàn)的:
public static void main(String[] args) {
//···
Looper.prepareMainLooper();
//···
Looper.loop();
//···
}
上述代碼已去掉無關代碼笙纤,至此我們就了解了Looper
的來歷,但之后調用的Looper#loop()
是個什么鬼?這個主要是進入一個不斷從MessageQueue
中獲取消息進行處理的無限循環(huán)组力,在分析這個循環(huán)之前我們要先來分析下MessageQueue
是個什么鬼省容。
4. MessageQueue是什么鬼
mQueue = new MessageQueue(quitAllowed);
上面創(chuàng)建Looper
的時新建了一個MessageQueue
對象,看下它的構造函數:
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
private native static long nativeInit();
WFK燎字,你就給我看這個P冉贰!候衍!C++學渣表示強烈抗議笼蛛。對于Native感興趣的小伙伴可以自行查看源碼,我跟著老羅的文章:Android應用程序消息處理機制(Looper蛉鹿、Handler)分析稍微感受了下源碼的邏輯滨砍,nativeInit
就是在native層創(chuàng)建了一個NativeMessageQueue
及一個Looper
對象,而native層的Looper
對象利用管道機制來監(jiān)控文件,從而可以利用epoll機制實現(xiàn)MessageQueue
的等待和喚醒惋戏,這個在MessageQueue#next()
會進一步分析领追。至此,MessageQueue
對象就被創(chuàng)建出來了(由于MessageQueue
的構建函數僅有包訪問權限响逢,因此正常情況下我們無需關心MessageQueue
的創(chuàng)建)绒窑,然后我們來看下Looper#loop()
到底在做什么。
5. Looper#loop()到底正在做什么
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
//···省略無關代碼
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//···省略無關代碼
msg.target.dispatchMessage(msg);
//···省略無關代碼
msg.recycleUnchecked();
}
}
Looper#loop()
方法只有當Looper
被創(chuàng)建出來之后方可調用舔亭,主要包括一個無限循環(huán):從MessageQueue
中獲取消息進行處理些膨,然后回收處理完的消息。我們先來看MessageQueue#next()
:
Message next() {
// 在Looper#loop()中我們知道返回空消息會退出loop()中的無限循環(huán)
// 當調用MessageQueue#quit(boolean)時會調用nativeDestory()銷毀MessageQueue分歇,將ptr置為0
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // 僅在第一次調用時為-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
/**
* 這個調用跟上面提到native層中Looper的epoll機制相關傀蓉,用于等待可處理的消息
* nextPollTimeoutMillis < 0 : 進入無限空閑等待,直到有新消息喚醒
* nextPollTimeoutMillis = 0 : 不等待
* nextPollTimeoutMillis > 0 : 進入空閑等待职抡,直到有新消息喚醒或者nextPollTimeoutMillis超時
**/
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 注:MessageQueue管理的消息是一個消息鏈表葬燎,后續(xù)Message中會詳細分析
if (msg != null && msg.target == null) {
/**
* msg.target為空是一類特殊消息(柵欄消息),用于阻塞所有同步消息缚甩,但是對異步消息沒有影響谱净,
* 后續(xù)會詳細分析。在這個前提下擅威,當頭部是特殊消息時需要往后找是否有異步消息
*/
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 找到消息
if (now < msg.when) {
// 消息的觸發(fā)時間在當前時間之后壕探,于是計算出需要等待的時間,準備進入有限空閑等待
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;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
// 沒找到消息,準備進入無限空閑等待
nextPollTimeoutMillis = -1;
}
// 沒有可處理的消息厉熟,并且消息隊列已經退出导盅,則返回空消息讓loop退出
if (mQuitting) {
dispose();
return null;
}
// 當mMessages為空或者mMessages的處理時間在當前時間之后(注意柵欄消息的特殊情況)時,
// 并且pendingIdleHandlerCount沒有在此處初始化過揍瑟,
// 則設置pendingIdleHandlerCount為IdleHandler的數量白翻,IdleHandler后續(xù)詳細說明。
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 無空閑處理器绢片,阻塞隊列滤馍,進入空閑等待
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 調用空閑處理器邏輯,此處代碼僅調用一次
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// 設置為0保證空閑處理器代碼僅調用一次
pendingIdleHandlerCount = 0;
// 在處理空閑處理器的時候可能已經有可處理的消息底循,因此無需等待
nextPollTimeoutMillis = 0;
}
}
由于這個函數是消息獲取的關鍵巢株,因此是無刪減版。而相關說明直接注釋在代碼中此叠,主要目的在于建議小伙伴們看源碼纯续。我相信讀完源碼應該對獲取消息的機制有了比較完整的了解随珠,當loop
從MessageQueue
中獲取到消息便可以進行消息處理并在處理后回收該消息,具體等我們分析完消息的發(fā)送之后再來看猬错。
6. Handler#sendMessage中的消息怎么獲取到
public final Message obtainMessage(){
return Message.obtain(this);
}
直接調用Message#obtain(Handler)
:
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
直接調用Message#obtain()
- -!:
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
這里會先從回收池中取消息窗看,如果沒有就新創(chuàng)建一條消息;回收池是一個消息鏈表倦炒,sPoolSync
是同步符號显沈,sPool
是鏈表頭,sPoolSize
是回收池中可用消息數逢唤,最大限制為MAX_POOL_SIZE
(默認為50)拉讯。這些都沒有太大可分析性,我們來看看Message
的結構:
public int what; // 消息碼鳖藕,帶Handler命名空間魔慷,因此不同Handler中相同消息碼不沖突
public int arg1; // 整數數據
public int arg2; // 整數數據
public Object obj; // 任意對象,當利用Messenger進行跨進程傳遞時需要繼承自Parcelable
public Messenger replyTo; // Messenger對象實現(xiàn)跨進程消息傳遞
public int sendingUid = -1; // 跨進程是標記消息來源的Uid
/**
* flags可設置消息是否在使用以及是否異步
* FLAG_IN_USE = 1 << 0著恩,該標記只有在創(chuàng)建或obtain時才會清除院尔,此時方可修改消息的相關數據及進行發(fā)送
* FLAG_ASYNCHRONOUS = 1 << 1,標記該消息為異步消息喉誊,不受柵欄消息的影響
**/
/*package*/ int flags;
/*package*/ long when; // 消息執(zhí)行時間邀摆,采用SystemClock#uptimeMillis()時間base
/*package*/ Bundle data; // 消息的數據
/*package*/ Handler target; // 消息對應的Handler
/*package*/ Runnable callback; // 消息對應的回調,具體參看下文中消息處理一節(jié)
/*package*/ Message next; // 形成消息鏈表伍茄,以在MessageQueue以及消息回收池中使用
至此栋盹,消息的來源以及消息的結構分析完畢,其中flags
由Messag
自己管理敷矫,data
由getData
例获、peekData
以及setData
進行管理,target
及callback
由Handler
中相關獲取或發(fā)送消息的接口管理曹仗。獲取到消息之后躏敢,便可以調用Handler
的消息發(fā)送接口進行發(fā)送,那是如何進入MessageQueue
的咧?
7. Handler#sendMessage中的消息怎么放入消息隊列
Handler
中的消息發(fā)送接口除了Handler#sendMessageAtFrontOfQueue(Message)
均會調用Handler#sendMessageAtTime(Message)
讥脐,而這兩個接口最終調用了Handler#enqueueMessage(MessageQueue, Message, long)
:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
根據Handler
的mAsynchronous
屬性設置消息的異步屬性遭居,最后調用MessageQueue#enqueueMessage(Message, long)
:
// 插入成功返回true,否則返回false
boolean enqueueMessage(Message msg, long when) {
// Handler中不允許發(fā)送target為空的消息旬渠,空消息為特殊消息(柵欄消息)
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 不允許發(fā)送狀態(tài)為使用中的消息
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
// 不允許發(fā)送消息給已退出的消息隊列
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", 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) {
// 消息觸發(fā)時間最早俱萍,直接插在鏈表頭部,如果當前隊列阻塞則喚醒消息隊列的等待告丢,見MessageQueue#next
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 將消息插入到鏈表中間枪蘑,如果鏈表頭是柵欄消息并且該消息是觸發(fā)時間最早的異步消息則需要進行喚醒
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
建議查看源碼,相關說明已注釋在代碼中。把新消息放入隊列岳颇,并在必要的時候喚醒消息隊列進行處理照捡,從而就回到上述MessageQueue#next
的邏輯中,然后在有可處理消息的時候將消息發(fā)送到Looper#loop
中進行處理及回收话侧。
8. Message的處理及回收
當消息被返回到loop
中時栗精,調用:
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
target
就是Handler
,于是調用:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
上述代碼表明:當消息本身回調不為空時則由消息本身回調處理該消息瞻鹏;當Handler
的mCallback
不為空時則由Handler
的mCallback
處理消息悲立;否則則由Handler
中的鉤子handleMessage
進行處理。消息處理完了之后需要將消息回收:
void recycleUnchecked() {
// 標記為使用中新博,清除所有數據
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) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
至此薪夕,整個Handler
的運行機制(創(chuàng)建Handler -> 獲取消息對象 -> 發(fā)送消息 -> 處理消息
)就分析完了。
9. 一些其他補充
9.1 柵欄消息
柵欄消息:target
為空的特殊消息赫悄,用于延遲MessageQueue
中所有指定時間之后的同步消息原献,異步消息則仍可執(zhí)行。發(fā)送和移除柵欄消息必須成對出現(xiàn)涩蜘,否則可能導致MessageQueue
被掛起嚼贡。
其發(fā)送移除接口在Looper中:
public int postSyncBarrier() {
return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
}
public void removeSyncBarrier(int token) {
mQueue.removeSyncBarrier(token);
}
調用了MessageQueue#enqueueSyncBarrier(long)
:
int enqueueSyncBarrier(long when) {
// 創(chuàng)建一個target為空的特殊消息,并根據when插入MessageQueue中合適的位置
// 無需喚醒因為柵欄消息的目的在于阻塞消息的執(zhí)行
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
必須成對出現(xiàn)的MessageQueue#removeSyncBarrier(token)
同诫,其中token
由enqueueSyncBarrier
返回:
void removeSyncBarrier(int token) {
// 移除token對應的柵欄消息粤策,并在必要的時候進行喚醒
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next;
needWake = false;
} else {
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
有沒有覺得,so easy - -误窖!
9.2 空閑處理器(IdleHandler)
IdleHandler
定義在MessageQueue
中:
public static interface IdleHandler {
// 返回true表示保持在MessageQueue的mIdleHandlers中
boolean queueIdle();
}
具體調用時機見MessageQueue#next
中的分析叮盘。
9.3 Handler的相關接口介紹
獲取消息(帶不同參數):
final Message obtainMessage()
final Message obtainMessage(int what)
final Message obtainMessage(int what, int arg1, int arg2)
final Message obtainMessage(int what, Object obj)
final Message obtainMessage(int what, int arg1, int arg2, Object obj)
發(fā)送消息:
final boolean sendEmptyMessage(int what)
final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
final boolean sendEmptyMessageDelayed(int what, long delayMillis)
final boolean sendMessage(Message msg)
final boolean sendMessageAtFrontOfQueue(Message msg)
final boolean sendMessageDelayed(Message msg, long delayMillis)
boolean sendMessageAtTime(Message msg, long uptimeMillis)
發(fā)送帶Callback的消息:
post(Runnable r)
final boolean postAtFrontOfQueue(Runnable r)
final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
final boolean postAtTime(Runnable r, long uptimeMillis)
final boolean postDelayed(Runnable r, long delayMillis)
移除消息:
final void removeCallbacks(Runnable r)
final void removeCallbacks(Runnable r, Object token)
final void removeCallbacksAndMessages(Object token)
final void removeMessages(int what)
final void removeMessages(int what, Object object)
9.4 HandlerThread類
HandlerThread
類是Handler
的一個輔助類,當調用HandlerThread#start()
之后霹俺,會創(chuàng)建一個帶Looper
的Thread
:
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
這樣柔吼,我們使用非主線程Handler
的時候便比較簡單了:
HandlerThread t = new HandlerThread(TAG);
t.start();
mHandler = new Handler(t.getLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// TODO: 處理消息
}
});