提起Android消息機(jī)制照瘾,想必都不陌生馅闽。其中包含三個(gè)部分:Handler蚊丐,MessageQueue以及Looper熙参,三者共同協(xié)作,完成消息機(jī)制的運(yùn)行麦备。本篇文章將由淺入深解析Android消息機(jī)制的運(yùn)行原理孽椰,先介紹三者之間的協(xié)作框架,然后從源碼的角度來分析具體的運(yùn)行機(jī)制凛篙。
一黍匾、消息機(jī)制概述
1.消息機(jī)制的簡(jiǎn)介
在Android中使用消息機(jī)制,我們首先想到的就是Handler呛梆。沒錯(cuò)锐涯,Handler是Android消息機(jī)制的上層接口。Handler的使用過程很簡(jiǎn)單填物,通過它可以輕松地將一個(gè)任務(wù)切換到Handler所在的線程中去執(zhí)行纹腌。通常情況下,Handler的使用場(chǎng)景就是更新UI滞磺。
如下就是使用消息機(jī)制的一個(gè)簡(jiǎn)單實(shí)例:
public class Activity extends android.app.Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
...............耗時(shí)操作
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
在子線程中升薯,進(jìn)行耗時(shí)操作,執(zhí)行完操作后击困,發(fā)送消息覆劈,通知主線程更新UI。這便是消息機(jī)制的典型應(yīng)用場(chǎng)景沛励。我們通常只會(huì)接觸到Handler和Message來完成消息機(jī)制责语,其實(shí)內(nèi)部還有兩大助手來共同完成消息傳遞。
2.消息機(jī)制的模型
消息機(jī)制主要包含:MessageQueue目派,Handler和Looper這三大部分坤候,以及Message,下面我們一一介紹企蹭。
Message:需要傳遞的消息白筹,可以傳遞數(shù)據(jù);
MessageQueue:消息隊(duì)列谅摄,但是它的內(nèi)部實(shí)現(xiàn)并不是用的隊(duì)列徒河,實(shí)際上是通過一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來維護(hù)消息列表,因?yàn)閱捂湵碓诓迦牒蛣h除上比較有優(yōu)勢(shì)送漠。主要功能向消息池投遞消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)顽照;
Handler:消息輔助類,主要功能向消息池發(fā)送各種消息事件(Handler.sendMessage)和處理相應(yīng)消息事件(Handler.handleMessage);
Looper:不斷循環(huán)執(zhí)行(Looper.loop)代兵,從MessageQueue中讀取消息尼酿,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者。
3.消息機(jī)制的架構(gòu)
消息機(jī)制的運(yùn)行流程:在子線程執(zhí)行完耗時(shí)操作植影,當(dāng)Handler發(fā)送消息時(shí)裳擎,將會(huì)調(diào)用MessageQueue.enqueueMessage
,向消息隊(duì)列中添加消息思币。當(dāng)通過Looper.loop
開啟循環(huán)后鹿响,會(huì)不斷地從線程池中讀取消息,即調(diào)用MessageQueue.next
谷饿,然后調(diào)用目標(biāo)Handler(即發(fā)送該消息的Handler)的dispatchMessage
方法傳遞消息惶我,然后返回到Handler所在線程,目標(biāo)Handler收到消息各墨,調(diào)用handleMessage
方法,接收消息启涯,處理消息贬堵。
MessageQueue,Handler和Looper三者之間的關(guān)系:每個(gè)線程中只能存在一個(gè)Looper结洼,Looper是保存在ThreadLocal中的黎做。主線程(UI線程)已經(jīng)創(chuàng)建了一個(gè)Looper,所以在主線程中不需要再創(chuàng)建Looper松忍,但是在其他線程中需要?jiǎng)?chuàng)建Looper蒸殿。每個(gè)線程中可以有多個(gè)Handler,即一個(gè)Looper可以處理來自多個(gè)Handler的消息鸣峭。 Looper中維護(hù)一個(gè)MessageQueue宏所,來維護(hù)消息隊(duì)列,消息隊(duì)列中的Message可以來自不同的Handler摊溶。
下面是消息機(jī)制的整體架構(gòu)圖爬骤,接下來我們將慢慢解剖整個(gè)架構(gòu)。
從中我們可以看出:
Looper有一個(gè)MessageQueue消息隊(duì)列莫换;
MessageQueue有一組待處理的Message霞玄;
Message中記錄發(fā)送和處理消息的Handler;
Handler中有Looper和MessageQueue拉岁。
二坷剧、消息機(jī)制的源碼解析
1.Looper
要想使用消息機(jī)制,首先要?jiǎng)?chuàng)建一個(gè)Looper喊暖。
初始化Looper
無參情況下惫企,默認(rèn)調(diào)用prepare(true);
表示的是這個(gè)Looper可以退出,而對(duì)于false的情況則表示當(dāng)前Looper不可以退出陵叽。雅任。
public static void prepare() {
prepare(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));
}
這里看出风范,不能重復(fù)創(chuàng)建Looper,只能創(chuàng)建一個(gè)沪么。創(chuàng)建Looper,并保存在ThreadLocal硼婿。其中ThreadLocal是線程本地存儲(chǔ)區(qū)(Thread Local Storage,簡(jiǎn)稱為TLS)禽车,每個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域寇漫,不同線程之間彼此不能訪問對(duì)方的TLS區(qū)域。
開啟Looper
public static void loop() {
final Looper me = myLooper(); //獲取TLS存儲(chǔ)的Looper對(duì)象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //獲取Looper對(duì)象中的消息隊(duì)列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { //進(jìn)入loop的主循環(huán)方法
Message msg = queue.next(); //可能會(huì)阻塞,因?yàn)閚ext()方法可能會(huì)無限循環(huán)
if (msg == null) { //消息為空殉摔,則退出循環(huán)
return;
}
Printer logging = me.mLogging; //默認(rèn)為null州胳,可通過setMessageLogging()方法來指定輸出,用于debug功能
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg); //獲取msg的目標(biāo)Handler逸月,然后用于分發(fā)Message
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
}
msg.recycleUnchecked();
}
}
loop()進(jìn)入循環(huán)模式栓撞,不斷重復(fù)下面的操作,直到消息為空時(shí)退出循環(huán):
讀取MessageQueue的下一條Message(關(guān)于next()碗硬,后面詳細(xì)介紹)瓤湘;
把Message分發(fā)給相應(yīng)的target。
當(dāng)next()取出下一條消息時(shí)恩尾,隊(duì)列中已經(jīng)沒有消息時(shí)弛说,next()會(huì)無限循環(huán),產(chǎn)生阻塞翰意。等待MessageQueue中加入消息木人,然后重新喚醒。
主線程中不需要自己創(chuàng)建Looper冀偶,這是由于在程序啟動(dòng)的時(shí)候醒第,系統(tǒng)已經(jīng)幫我們自動(dòng)調(diào)用了Looper.prepare()
方法。查看ActivityThread中的main()
方法进鸠,代碼如下所示:
public static void main(String[] args) {
..........................
Looper.prepareMainLooper();
..........................
Looper.loop();
..........................
}
其中``prepareMainLooper()方法會(huì)調(diào)用
prepare(false)`方法淘讥。
2.Handler
創(chuàng)建Handler
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
.................................
//必須先執(zhí)行Looper.prepare(),才能獲取Looper對(duì)象堤如,否則為null.
mLooper = Looper.myLooper(); //從當(dāng)前線程的TLS中獲取Looper對(duì)象
if (mLooper == null) {
throw new RuntimeException("");
}
mQueue = mLooper.mQueue; //消息隊(duì)列蒲列,來自Looper對(duì)象
mCallback = callback; //回調(diào)方法
mAsynchronous = async; //設(shè)置消息是否為異步處理方式
}
對(duì)于Handler的無參構(gòu)造方法,默認(rèn)采用當(dāng)前線程TLS中的Looper對(duì)象搀罢,并且callback回調(diào)方法為null蝗岖,且消息為同步處理方式。只要執(zhí)行的Looper.prepare()
方法榔至,那么便可以獲取有效的Looper對(duì)象抵赢。
3.發(fā)送消息
發(fā)送消息有幾種方式,但是歸根結(jié)底都是調(diào)用了sendMessageAtTime()
方法。
在子線程中通過Handler的post()方式或send()方式發(fā)送消息铅鲤,最終都是調(diào)用了sendMessageAtTime()
方法划提。
post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
{
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
send方法
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
就連子線程中調(diào)用Activity中的runOnUiThread()中更新UI,其實(shí)也是發(fā)送消息通知主線程更新UI邢享,最終也會(huì)調(diào)用sendMessageAtTime()
方法鹏往。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
如果當(dāng)前的線程不等于UI線程(主線程),就去調(diào)用Handler的post()方法骇塘,最終會(huì)調(diào)用sendMessageAtTime()
方法伊履。否則就直接調(diào)用Runnable對(duì)象的run()方法。
下面我們就來一探究竟款违,到底sendMessageAtTime()
方法有什么作用唐瀑?
sendMessageAtTime()
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//其中mQueue是消息隊(duì)列,從Looper中獲取的
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//調(diào)用enqueueMessage方法
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//調(diào)用MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到sendMessageAtTime()`方法的作用很簡(jiǎn)單插爹,就是調(diào)用MessageQueue的enqueueMessage()方法哄辣,往消息隊(duì)列中添加一個(gè)消息。
下面來看enqueueMessage()方法的具體執(zhí)行邏輯赠尾。
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
// 每一個(gè)Message必須有一個(gè)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) { //正在退出時(shí)力穗,回收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的觸發(fā)時(shí)間是隊(duì)列中最早的萍虽, 則進(jìn)入該該分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//將消息按時(shí)間順序插入到MessageQueue睛廊。一般地形真,不需要喚醒事件隊(duì)列杉编,除非
//消息隊(duì)頭存在barrier,并且同時(shí)Message是隊(duì)列中最早的異步消息咆霜。
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是按照Message觸發(fā)時(shí)間的先后順序排列的邓馒,隊(duì)頭的消息是將要最早觸發(fā)的消息。當(dāng)有消息需要加入消息隊(duì)列時(shí)蛾坯,會(huì)從隊(duì)列頭開始遍歷光酣,直到找到消息應(yīng)該插入的合適位置,以保證所有消息的時(shí)間順序脉课。
4.獲取消息
當(dāng)發(fā)送了消息后救军,在MessageQueue維護(hù)了消息隊(duì)列,然后在Looper中通過loop()
方法倘零,不斷地獲取消息唱遭。上面對(duì)loop()
方法進(jìn)行了介紹,其中最重要的是調(diào)用了queue.next()
方法,通過該方法來提取下一條信息呈驶。下面我們來看一下next()
方法的具體流程拷泽。
next()
Message next() {
final long ptr = mPtr;
if (ptr == 0) { //當(dāng)消息循環(huán)已經(jīng)退出,則直接返回
return null;
}
int pendingIdleHandlerCount = -1; // 循環(huán)迭代的首次為-1
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//阻塞操作,當(dāng)?shù)却齨extPollTimeoutMillis時(shí)長(zhǎng)司致,或者消息隊(duì)列被喚醒拆吆,都會(huì)返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
//當(dāng)消息Handler為空時(shí),查詢MessageQueue中的下一條異步消息msg脂矫,為空則退出循環(huán)枣耀。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//當(dāng)異步消息觸發(fā)時(shí)間大于當(dāng)前時(shí)間,則設(shè)置下一次輪詢的超時(shí)時(shí)長(zhǎng)
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;
//設(shè)置消息的使用狀態(tài)奕枢,即flags |= FLAG_IN_USE
msg.markInUse();
return msg; //成功地獲取MessageQueue中的下一條即將要執(zhí)行的消息
}
} else {
//沒有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
...............................
}
}
nativePollOnce是阻塞操作佩微,其中nextPollTimeoutMillis代表下一個(gè)消息到來前缝彬,還需要等待的時(shí)長(zhǎng);當(dāng)nextPollTimeoutMillis = -1時(shí)哺眯,表示消息隊(duì)列中無消息谷浅,會(huì)一直等待下去。
可以看出next()
方法根據(jù)消息的觸發(fā)時(shí)間奶卓,獲取下一條需要執(zhí)行的消息,隊(duì)列中消息為空時(shí)一疯,則會(huì)進(jìn)行阻塞操作。
5.分發(fā)消息
在loop()方法中夺姑,獲取到下一條消息后墩邀,執(zhí)行msg.target.dispatchMessage(msg)
,來分發(fā)消息到目標(biāo)Handler對(duì)象盏浙。
下面就來具體看下dispatchMessage(msg)
方法的執(zhí)行流程眉睹。
dispatchMessage()
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//當(dāng)Message存在回調(diào)方法,回調(diào)msg.callback.run()方法废膘;
handleCallback(msg);
} else {
if (mCallback != null) {
//當(dāng)Handler存在Callback成員變量時(shí)竹海,回調(diào)方法handleMessage();
if (mCallback.handleMessage(msg)) {
return;
}
}
//Handler自身的回調(diào)方法handleMessage()
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
分發(fā)消息流程:
當(dāng)Message的msg.callback
不為空時(shí)丐黄,則回調(diào)方法msg.callback.run()
;
當(dāng)Handler的mCallback
不為空時(shí)斋配,則回調(diào)方法mCallback.handleMessage(msg)
;
最后調(diào)用Handler自身的回調(diào)方法handleMessage()
灌闺,該方法默認(rèn)為空艰争,Handler子類通過覆寫該方法來完成具體的邏輯。
消息分發(fā)的優(yōu)先級(jí):
Message的回調(diào)方法:message.callback.run()
桂对,優(yōu)先級(jí)最高甩卓;
Handler中Callback的回調(diào)方法:Handler.mCallback.handleMessage(msg)
,優(yōu)先級(jí)僅次于1接校;
Handler的默認(rèn)方法:Handler.handleMessage(msg)
猛频,優(yōu)先級(jí)最低狮崩。
對(duì)于很多情況下,消息分發(fā)后的處理方法是第3種情況鹿寻,即Handler.handleMessage()
睦柴,一般地往往通過覆寫該方法從而實(shí)現(xiàn)自己的業(yè)務(wù)邏輯。
三毡熏、總結(jié)
以上便是消息機(jī)制的原理坦敌,以及從源碼角度來解析消息機(jī)制的運(yùn)行過程×》ǎ可以簡(jiǎn)單地用下圖來理解狱窘。