Android從某種意義上看是一個以消息驅(qū)動的系統(tǒng)识椰,內(nèi)部含有大量以消息驅(qū)動的當時進行的交互陡厘,比如四大組件的啟動校哎、又比如常見的將子線程的任務(wù)切換到Handler所在的主線程中執(zhí)行等等吏夯,它屬于進程內(nèi)部的一種通信方式静浴。這篇文章就對android的消息機制做一個簡單的梳理磷籍。
一适荣、關(guān)鍵類簡介
消息機制主要涉及Looper/MessageQueue/Message/Handler/ThreadLocal這幾個類丙躏。
Looper:消息泵。一個死循環(huán)束凑,有消息來就處理晒旅,沒有消息就等待。一個線程最多只能有一個Looper對象汪诉,一個Looper對應(yīng)管理此線程的MessageQueue废恋,兩者一一對應(yīng)。
MessageQueue:消息隊列扒寄。內(nèi)部存儲了一組消息鱼鼓,以隊列的形式對外提供向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息(MessageQueue.next)的工作,內(nèi)部采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來存儲消息列表该编。
Message:消息迄本。分為硬件產(chǎn)生的消息(如按鈕、觸摸)和軟件生成的消息课竣。
Handler:消息處理者嘉赎。主要向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage);
ThreadLocal:一個線程內(nèi)部的數(shù)據(jù)存儲類于樟。保證線程內(nèi)部數(shù)據(jù)在各線程間相互獨立公条。
先看看消息機制整體流程:
二、源碼分析
2.1 Looper
Looper的字面意思是“循環(huán)者”迂曲,它被設(shè)計用來使一個普通線程變成Looper線程靶橱。所謂Looper線程就是循環(huán)工作的線程。
主要方法有兩個:
1) prepare() : 使Thread變成looper線程路捧,具備循環(huán)的能力
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));
}
看這個方法关霸,就是創(chuàng)建一個Looper放到當前Thread對應(yīng)的sThreadLocal里去。ThreadLocal前面講過杰扫,它主要作用就是讓線程間存儲數(shù)據(jù)相互獨立队寇,顯然這里它保證的是線程對應(yīng)的 Looper一一對應(yīng)且相互獨立。
我們再看看Looper的構(gòu)造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper內(nèi)包含了一個初始化的MessageQueue涉波,也關(guān)聯(lián)了當前的Thread英上。
那么總結(jié)下關(guān)系就是:一個Thread對應(yīng)的ThreadLocal保持了對應(yīng)的Looper,而Looper關(guān)聯(lián)了MessageQueue與當前Thread啤覆。
2) loop():
public static void loop() {
final Looper me = myLooper();//獲取當前線程本地存儲區(qū)存儲的 Looper對象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲取Looper對象對應(yīng)的消息隊列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {//looper主循環(huán)方法,是個死循環(huán)
Message msg = queue.next(); // 取走消息池的消息去處理惭聂,可能會阻塞
...
try {
msg.target.dispatchMessage(msg);//分發(fā)Message
}
...
msg.recycleUnchecked();//將消息放入消息池
}
}
loop()進入循環(huán)模式窗声,不斷重復下面的操作,直到?jīng)]有消息時退出循環(huán)
- 讀取MessageQueue的下一條Message辜纲; Message msg = queue.next();
- 把Message分發(fā)給相應(yīng)的target笨觅;msg.target.dispatchMessage(msg)拦耐;
- 再把分發(fā)后的Message回收到消息池,以便重復利用见剩。msg.recycleUnchecked()杀糯;
總結(jié): Looper主要就是讓線程具備消息泵的循環(huán)能力,僅此而已苍苞。
2.2MessageQueue
MessageQueue看名字像是一個隊列, 其實是以鏈表的形式保存message固翰。其次,MessageQueue是消息機制的Java層和C++層的連接紐帶羹呵,大部分核心方法都交給native層來處理骂际。
例如消息的阻塞處理:
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
在此我先只關(guān)心幾個方法:
1) next():
之前在Looper 的loop()死循環(huán)中見過此方法,作用是取出消息進行處理冈欢,而且有可能會阻塞歉铝。
下面我們來看看源碼,方法稍微有點長:
Message next() {
final long ptr = mPtr;
if (ptr == 0) {//當消息循環(huán)已經(jīng)退出凑耻,直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 循環(huán)迭代的首次為-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作太示,當?shù)却齨extPollTimeoutMillis時長,或者消息隊列被喚醒香浩,都會返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//當異步消息觸發(fā)的時間大于當前時間先匪,則設(shè)置下一次輪詢的超時時長
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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
//沒有消息,當nextPollTimeoutMillis = -1時弃衍,表示消息隊列中無消息呀非,會一直等待下去
nextPollTimeoutMillis = -1;
}
// 消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
//當消息隊列為空,或者是消息隊列的第一個消息時
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//沒有idle handlers 需要運行镜盯,則循環(huán)并等待岸裙。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//只有第一次循環(huán)時,會運行idle handlers速缆,執(zhí)行完成后降允,重置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時執(zhí)行的方法
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置idle handler個數(shù)為0,以保證不會再次重復運行
pendingIdleHandlerCount = 0;
//當調(diào)用一個空閑handler時艺糜,一個新message能夠被分發(fā)剧董,因此無需等待可以直接查詢pending message.
nextPollTimeoutMillis = 0;
}
}
nativePollOnce是阻塞操作,其中nextPollTimeoutMillis代表下一個消息到來前破停,還需要等待的時長翅楼;當nextPollTimeoutMillis = -1時,表示消息隊列中無消息真慢,會一直等待下去毅臊。
當處于空閑時,往往會執(zhí)行IdleHandler中的方法黑界。當nativePollOnce()返回后管嬉,next()從mMessages中提取一個消息皂林。
nativePollOnce()在native做了大量的工作。
2) enqueueMessage:
該方法是添加一條消息到消息隊列蚯撩,下面具體來看看實現(xiàn):
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,加入到消息池
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, 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) {
//p==null代表MessageQueue沒消息胎挎,或者msg的觸發(fā)時間是隊列中最早的沟启,則進入該分支
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; // invariant: p == prev.next
prev.next = msg;
}
// 消息沒有退出浦辨,我們認為此時mPtr != 0
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
MessageQueue是按照Message觸發(fā)時間的先后順序排列的,隊頭的消息是將要最早觸發(fā)的消息沼沈。當有消息需要加入消息隊列時流酬,會從隊列頭開始遍歷,直到找到消息應(yīng)該插入的合適位置列另,以保證所有消息的時間順序芽腾。
3) removeMessage:
方法顧名思義,從MessageQueue中移除消息:
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循環(huán),第一個循環(huán)是從隊頭開始店乐,移除符合條件的消息艰躺,第二個循環(huán)是從頭部移除完連續(xù)的滿足條件的消息之后,再從隊列后面繼續(xù)查詢是否有滿足條件的消息需要被移除眨八。
4) postSyncBarrier和removeSyncBarrier
攔截同步消息和取消攔截腺兴,可以自行研究。
總結(jié):MessageQueue就是一個鏈表結(jié)構(gòu)的消息池廉侧,內(nèi)部按照Message觸發(fā)時間的先后順序排列页响,提供基本的投遞、獲取段誊、刪除消息的功能闰蚕。
2.3 Message
message 其實就是對消息封裝的對象。針對它枕扫,主要了解的是消息池的概念陪腌,以及獲取消息和回收消息的兩個方法。
- 消息池:
message引入了消息池烟瞧,這樣的好處是诗鸭,當消息池不為空時,可以直接從消息池中獲取Message對象参滴,而不是直接創(chuàng)建强岸,提高效率。類似于設(shè)計模式中享元模式這么一個概念砾赔。
靜態(tài)變量sPool的數(shù)據(jù)類型為Message蝌箍,通過next成員變量,維護一個消息池暴心;靜態(tài)變量MAX_POOL_SIZE代表消息池的可用大屑嗣ぁ;消息池的默認大小為50专普。
- 消息池的主要操作obtain()和recycle()悯衬。
- 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(); //當消息池為空檀夹,則直接創(chuàng)建Message對象
}
obtain(): 從消息池取Message筋粗,都是把消息池表頭的Message取走,再把表頭指向next;
- recycle方法:把不再使用的消息加入消息池
public void recycle() {
if (isInUse()) {//判斷消息是否正在使用
if (gCheckRecycle) {// 版本>Android5.0 = true 版本<=Android5.0 = false
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
//對于不再使用的消息炸渡,加入消息池
void recycleUnchecked() {
//將消息標志位設(shè)為IN_USE,并清空消息所有參數(shù)
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) {//當消息池沒有滿時娜亿,將Message對象加入消息池
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;//消息池可用大小+1
}
}
}
recycle(): 將Message加入到消息池的過程,都是把Message加到鏈表的表頭蚌堵;
總結(jié): Message 就是消息對象本身买决,內(nèi)部有對象池,能一定程度優(yōu)化頻繁創(chuàng)建和銷毀消息帶來的性能問題吼畏。
2.4 Handler
Handler是消息處理者督赤,由它發(fā)起消息操作。主要扮演了往MessageQueue上添加消息和處理消息的角色宫仗。
首先看看Handler的構(gòu)造方法:
public class Handler {
final Looper mLooper;
final MessageQueue mQueue;
final Callback mCallback;
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());
}
}
mLooper = Looper.myLooper(); //默認關(guān)聯(lián)當前線程的Looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//關(guān)聯(lián)當前線程Looper對應(yīng)的MessageQueue
mCallback = callback;
mAsynchronous = async;
}
}
那么我們知道了够挂,Handler在哪個線程被創(chuàng)建就屬于哪個線程,就為哪個線程服務(wù)藕夫,因為關(guān)聯(lián)的是當前線程的Looper孽糖,操作的也就是當前線程的MessageQueue
Handler發(fā)送和處理消息:
Handler最核心的功能就兩個,一個是向MessageQueue發(fā)送消息毅贮,一個是處理MessageQueue分發(fā)出來的消息办悟。
1) 發(fā)送消息:
handler有許多發(fā)送消息的方法:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
光看這些方法參數(shù)你可能會覺得handler能發(fā)兩種消息,一種是Runnable對象滩褥,一種是message對象病蛉,這是直觀的理解,但其實post發(fā)出的Runnable對象最后都被封裝成message對象了。
以post()方法為例:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
最終還是封裝成了Message,而且還是從Message的消息池里取的铺然。
另外俗孝,這些發(fā)送消息的方法最終都會走到如下方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//把當前msg的target與當前Handler一一對應(yīng)
if (mAsynchronous) {//這個值是在Handler的構(gòu)造方法中設(shè)置是否異步
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//最終發(fā)送消息
}
2) 處理消息
消息的處理是通過核心方法dispatchMessage與鉤子方法handleMessage完成的
在Looper的 loop() 方法中:
msg.target.dispatchMessage(msg); 也就是執(zhí)行當前Hanlder的dispatchMessage(msg)方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
都是回調(diào)方法的處理,邏輯很明顯魄健,不贅述了赋铝。
最后一張圖看整個Java層面消息處理流程: