??在學(xué)習(xí)Android過程中峰伙,Android的消息機制是必須需要掌握的。樓主也只能算是一個Android的初學(xué)者,對Android的消息機制掌握的也不算很好。這里記錄一下我自己對Android消息機制的理解蜂林,錯誤之處,希望各位指正拇泣。
??本文參考文章:
??1.Android 異步消息處理機制 讓你深入理解 Looper噪叙、Handler、Message三者關(guān)系
??2.Android應(yīng)用程序消息處理機制(Looper挫酿、Handler)分析
1.概述
??要講消息處理機制构眯,不得不講的就是Handler、Looper早龟、MessageQueue這個重量級的人物惫霸。在這里對他們做一個概述,待會會詳細的介紹他們葱弟。
??從大體上來說壹店,Handler的主要作用就是發(fā)送Message和處理Message;Looper的作用就是不斷從MessageQueue(消息隊列)中取Message芝加,然后交給Handler處理硅卢;MessageQueue存放由Handler發(fā)送的Message。通常來說藏杖,Message里面就是需要主線程執(zhí)行的操作将塑,比如,我們從網(wǎng)絡(luò)獲取的文字內(nèi)容信息蝌麸,需要顯示到TextView上面去点寥。
2.Handler
??首先我們從上面走下去,從Handler開始走来吩。
??通常來說敢辩,使用Handler來處理異步消息,有基本的步驟弟疆。這里做一個簡單的例子戚长,在線程中發(fā)送一條String,讓TextView來顯示怠苔。
private Button mButton = null;
//創(chuàng)建一個Handler的對象同廉,注意我們重寫了handleMessage消息
//handleMessage方法就是用來處理我們發(fā)送的消息
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
mButton.setText(msg.obj.toString());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button) findViewById(R.id.button);
mButton.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button: {
sendMessage();
break;
}
}
}
private void sendMessage() {
new Thread(new Runnable() {
@Override
public void run() {
//創(chuàng)建一個Message的對象
Message message = Message.obtain();
message.obj = "我被點擊了";
//發(fā)送消息
mHandler.sendMessage(message);
}
}).start();
}
??上面代碼中,我相信不難吧。現(xiàn)在我們需要理解的是Handler是怎么將一個Message從子線程傳遞到主線程中恤溶,然后執(zhí)行的乓诽!
(1).sendMessage方法
??我們需要理解其中的緣由,我們來sendMessage方法的代碼咒程。
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
??我們在一步一步的跟蹤下去鸠天,最終到了enqueueMessage方法中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
??其中,這個方法的三個參數(shù),queue表示message需要進入的消息隊列帐姻,msg就是我們之前創(chuàng)建的Message對象稠集,uptimeMillis表示延遲時間。延遲時間這里不重要饥瓷,我們先不看剥纷。
??我們從源代碼中可以看出,首先msg.target = this呢铆。這句話將一個handler賦值到message的target屬性晦鞋,這一步非常重要,后面需要用的棺克。這里我們先記住悠垛,這里的意思就是,表示當(dāng)前的這個message屬于哪個handler娜谊,因為有可能有很多的handler都在發(fā)送消息确买!
?? mAsynchronous我們不看,這里不重要纱皆。最后是調(diào)用了queue.enqueueMessage(msg, uptimeMillis)方法湾趾,從這個方法的字面意思中,我們就可以看出來派草,這個方法是將一個message添加到queue的消息隊列搀缠。
??我們先不看queue里面的代碼。我們先來總結(jié)我們得到的信息近迁。
??1.我們在子線程中發(fā)送一個Message胡嘿,最終會進入到一個queue的消息隊列中去。
??2.在Message進入消息隊列之前钳踊,使用target字段來標(biāo)記了當(dāng)前的Message是哪個Handler發(fā)送的。
??到這里勿侯,我們好像沒有收獲拓瞪,不急,我們來看看在構(gòu)造一個Handler的時候助琐,給我們創(chuàng)建那些東西祭埂!
(2).Handler的構(gòu)造方法
??Handler的構(gòu)造方法最終調(diào)用到了Handler(Callback callback, boolean async)方法里面。
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();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
??在Handler的構(gòu)造方法中,我們發(fā)現(xiàn)了幾個小細節(jié)蛆橡,mLooper舌界、mQueue都被賦值了。Looper我們待會講泰演,我們在調(diào)用sendMessage方法的時候呻拌,發(fā)現(xiàn)其中調(diào)用到這一步:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
??我們注意到的是,之前的enqueueMessage里面的queue就是mQueue睦焕。
??到這里藐握,我們知道了:
??1.Handler在創(chuàng)建的時候,會創(chuàng)建一個MessageQueue和一個Looper垃喊,也就是說猾普,一個Handler同時綁定一個MessageQueue和一個Looper。
??2.我們在調(diào)用sendMessage等方法來發(fā)送消息的時候本谜,消息進入的消息隊列就是發(fā)送消息的Handler自己的MessageQueue初家。
2.Looper
??記得在概述中描述,Handler是發(fā)送消息和處理消息乌助,現(xiàn)在我們對發(fā)送消息有了一個簡單的認(rèn)識溜在,但是我們對處理消息沒有介紹。不急眷茁,要想知道處理消息炕泳,我們必須先知道,消息是怎么被獲取出來的上祈,難道是Handler直接伸手從MessageQueue中去拿嗎培遵?怎么可能這么簡單嗎?接下來登刺,我們介紹一下Looper籽腕,又一位重量級的人物
(1).Looper構(gòu)造方法
??我們先來看看Looper的構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
??我們在這個構(gòu)造方法里面發(fā)現(xiàn),Looper創(chuàng)建一個MessageQueue纸俭。但是除了這個信息皇耗,似乎沒看其他有用的信息了。
??怎么辦揍很?通常來說使用Looper不會直接的創(chuàng)建郎楼,從這里我們也看到它的構(gòu)造方法是private的,而是調(diào)用prepare方法窒悔。
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));
}
??在prepare方法中呜袁,需要注意的是:sThreadLocal變量是一個ThreadLocal變量。這種類型的變量有什么呢简珠?具體的解釋大家可以到網(wǎng)上去找(其實我也不是很懂阶界,哈哈!!1烊凇)芙粱。這里簡單的解釋一下,ThreadLocal里面封裝了一個變量氧映,用來存儲相應(yīng)的信息春畔,關(guān)鍵是不同的線程從ThreadLocal里面獲取的變量是不同的,每個線程從ThreadLocal獲取的是自己的變量屯耸,線程之間的是不會相互影響拐迁。
??從這里,我們可以得到是疗绣,每個線程都會有一個自己的Looper對象线召。
??到這里,我們知道:
??1.Looper的對象不能直接new多矮,而是調(diào)用靜態(tài)方法prepare方法來創(chuàng)建
??2.同一個線程不能調(diào)用兩次prepare方法缓淹,因為Looper對象保存在一個ThreadLocal對象。
??3.從之前的Handler中塔逃,我們可以看到Handler里面持有了一個Looper對象(通過調(diào)用方法mLooper = Looper.myLooper())讯壶。這個對象就是TreadLocal封裝的對象。從而得出湾盗,如果這個Handler在主線程中創(chuàng)建的伏蚊,那么Looper肯定在屬于主線程。
??從上面的結(jié)論中格粪,我們可以得出躏吊,如果我們在一個線程中創(chuàng)建一個Handler,必須先創(chuàng)建當(dāng)前線程的Looper對象帐萎。但是我們比伏,你會發(fā)現(xiàn),之前那個例子中疆导,我們直接在一個Activity中創(chuàng)建一個Handler對象赁项,而沒有創(chuàng)建主線程的Looper,這個為什么沒有報錯呢澈段?我們可以假設(shè)悠菜,我們在創(chuàng)建Handler時,主線程的Looper早已經(jīng)被prepare了败富!但是具體在哪里調(diào)用的呢悔醋?待會我們會展示!
(2).loop方法
??在之前說過囤耳,Looper的作用就是不斷從MessageQueue(消息隊列)中取Message,然后交給Handler處理。但是我們到現(xiàn)在還不知道Looper到底是怎么從MessageQueue里面去消息的充择,這個就得引出我們的loop方法德玫。
??我們先來看看loop方法的源代碼,沒有貼出完整的代碼,而是刪除了一些自己認(rèn)為不重要的代碼:
public static void loop() {
//取得當(dāng)前線程的Looper--還記得我們之前的說的ThreadLocal變量椎麦,這個變量里面保存的就是當(dāng)前線程的
//Looper對象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//獲取當(dāng)前Looper的消息隊列對象
final MessageQueue queue = me.mQueue;
// 死循環(huán)
for (;;) {
//如果沒有消息的話宰僧,當(dāng)前線程會被阻塞住,
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
//將當(dāng)前的Message分發(fā)到Message自己的Handler
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
??我們先來整理一下观挎,這個方法主要描述內(nèi)容:
??1.首先獲取當(dāng)前線程的Looper對象和MessageQueue對象
??2.從MessageQueue里面去獲取Message琴儿;如果獲取到Message,則分發(fā)到Message自己的Handler去處理嘁捷;反之造成,沒有獲取到信息,線程會被阻塞在next方法里面雄嚣。
??3.通過Handler的dispatchMessage方法將獲取的到Message從MessageQueue傳遞到了Handler晒屎,讓Handler去處理。
??到這里缓升,我們來整理一下思路鼓鲁。首先,在主線程創(chuàng)建了一個Handler港谊,表示當(dāng)前的Looper是放在主線程中的骇吭,當(dāng)然MessageQueue也是屬于主線程的。這里需要說明的是歧寺,就是一個線程只能有一個Looper燥狰,由于MessageQueue在Looper里面,所以MessageQueue也是只有一個成福,但是一個線程中Handler可以會被多次創(chuàng)建碾局,所以,屬于同一個線程的Handler中持有的是同一個MessageQueue和Looper奴艾。
??我們使用在主線程中創(chuàng)建的Handler通過調(diào)用sendMessage等方法來發(fā)送消息净当,最終進入的消息隊列是主線程的MessageQueue。我們在Looper的loop方法中獲取Message蕴潦,然后將Message分發(fā)到Message的target像啼,也就是發(fā)送Message的Handler,讓它來處理。
??整個消息傳遞的過程就是這樣的潭苞,這個就能解釋忽冻,為什么我們在子線程中發(fā)送一個Message,在主線程的Handler能接收得到此疹,從而進行對消息處理僧诚。因為Handler本身就是屬于主線程遮婶,包括Handler里面持有的Looper和MessageQueue都是屬于主線程。
??這里有一個疑問湖笨,那就是旗扑,我們知道我們在主線程中創(chuàng)建Handler時,其實主線程的Looper早就已經(jīng)創(chuàng)建好了慈省,這一點是怎么看出來得呢臀防?
??例如:
new Thread(new Runnable() {
@Override
public void run() {
//這里將Looper的prepare方法注釋了
//表示當(dāng)前的線程Looper沒有被初始化
// Looper.prepare();
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i("pby123", msg.obj.toString() + " " + Thread.currentThread().getName());
}
};
Message message = Message.obtain();
message.obj = "pby123";
mHandler.sendMessage(message);
}
}).start();
??然后我們來運行一下程序,會發(fā)現(xiàn)一個臭名昭著的異常边败!
??這個異常我們應(yīng)該不陌生袱衷,因為凡是要使用Looper的地方,都用check一下Looper是否preapre完成笑窜。
??上面的樣例致燥,我們是從一個新的線程里面創(chuàng)建Handler,由于是新的線程怖侦,就必須將這個線程的Looper和MessageQueue創(chuàng)建好篡悟。
??從而得出,我們在主線程中可以直接創(chuàng)建Handler匾寝,表示主線程的Looper和MessageQueue在我們創(chuàng)建Handler之前就已經(jīng)創(chuàng)建完畢了残腌!
??那到底是在哪里創(chuàng)建主線程的Looper导盅。這個得引出ActivityThread類的main方法环凿,我們都知道m(xù)ain方法是Java程序的入口讥电,我們來看看main方法:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
??是不是看到main方法就感覺非常的親切,終于可以回到當(dāng)初學(xué)習(xí)Java的時候了猜年。我也是抡锈,看到main方法之后,感覺之前有好多的問題都解決了乔外!
??在main方法里面床三,我們看到:Looper.prepareMainLooper();這一句話的意思就是初始化的主線程的Looper。從這里杨幼,我們知道了撇簿,為什么我們在主線程里面可以創(chuàng)建Handler,而不用去prepare Looper差购。
??同時四瘫,我們還注意的是,Looper.loop()欲逃。我們之前已經(jīng)分析過了找蜜,loop方法主要是在MessageQueue里面去取消息來處理。由于這里是主線程的Looper稳析,所以這里的loop方法是從主線程的MessageQueue里面取Message洗做。
??之前弓叛,我們使用一個在主線程創(chuàng)建的Handler在一個子線程發(fā)送Message,最后發(fā)送到了主線程的MessageQueue诚纸。但是什么時候調(diào)用的loop呢邪码?其實就是在main方法里面調(diào)用了的。
??同樣的道理咬清,如果我們在一個新的線程里面分別創(chuàng)建Handler、Looper奴潘、MessageQueue旧烧,當(dāng)調(diào)用Handler的sendMessage方法來發(fā)送一條Message,在Handler的handleMessage方法是不會接收到這個Message對象画髓,這個是由于Looper的loop方法啟動掘剪,當(dāng)前線程
??我們知道,loop方法是一個死循環(huán)奈虾,但是為什么不會導(dǎo)致應(yīng)用ANR呢?(Application Not Response)夺谁。其實網(wǎng)絡(luò)上有很多的解釋,可能是自己太笨了肉微,理解不了那些思路匾鸥。下面將來結(jié)合Looper的loop方法和MessageQueue的部分方法來解釋一下原因。
4.MessageQueue
??我們知道MessageQueue相當(dāng)于是一個存放Message的容器碉纳,Handler往這個容器里面放入Message勿负,Looper從這個容器取數(shù)據(jù)。我們先來看看存放數(shù)據(jù)的部分
??在Looper的loop方法里面有這么一句:
Message msg = queue.next(); // might block
??上面這一句劳曹,就是Looper從MessageQueue里面取Message的操作奴愉,我們注意一下官方的注釋,這一句可能會導(dǎo)致阻塞铁孵!
??那么什么情況下會被阻塞呢锭硼?是不是當(dāng)前MessageQueue為空的時候,會被阻塞呢蜕劝?如果MessageQueue為空時檀头,會被阻塞,這一句又是什么作用呢熙宇?
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
(1).next方法
??我們先來看看next方法鳖擒,這里我先將next的所有代碼貼出,然后一部分一部分的分析
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
??首先烫止,我們來看看這一句話
nativePollOnce(ptr, nextPollTimeoutMillis);
??根據(jù)Android應(yīng)用程序消息處理機制(Looper蒋荚、Handler)分析中介紹,nativePollOnce方法是用來表示當(dāng)前線程會被阻塞,nextPollTimeoutMillis表達的意思就是當(dāng)前的線程需需要阻塞多久馆蠕。其中如果nextPollTimeoutMillis為0期升,表示不阻塞惊奇;-1表示無限期的阻塞,除非有線程喚醒它播赁;其他的正數(shù)值表示阻塞多久颂郎。
??然后我們來看看下面的代碼
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
??其中,msg表示當(dāng)前的消息隊列中第一個可以用的Message容为。其中先判斷了當(dāng)前的時間與消息執(zhí)行的時間的大小乓序,如果表示當(dāng)前的時間小于Message執(zhí)行的時間,也就是說坎背,還沒有到達該Message執(zhí)行的時間替劈,因此給nextPollTimeoutMillis賦值,表示線程要阻塞多久得滤。
??但是陨献,如果當(dāng)前的時間符合Message的執(zhí)行時間了,就得馬上返回這個Message懂更,去執(zhí)行里面的操作眨业。
??如果當(dāng)前取得的第一個Message是空的,表示當(dāng)前的MessageQueue里面沒有了Message了沮协,所以將nextPollTimeoutMills重置為-1龄捡,表示當(dāng)前將無限期的阻塞下去,除非其他的線程將喚醒慷暂!
??其中我們還需要注意的是:
if (mQuitting) {
dispose();
return null;
}
??這個return是整個next方法里面第二個return null墅茉,還記得我們在loop方法里面的一段代碼嗎?就是如果從next方法里面獲取的方法是null的話呜呐,那么直接退出了loop循環(huán)就斤。這個return null的返回條件就是就能解釋我們之前的那個疑問--如果MessageQueue為空時,會被阻塞蘑辑,這一句又是什么作用呢洋机?
??第一個return null的條件是mPtr等于0時,關(guān)于mPtr等于0是表示什么情況洋魂,我也不是清楚绷旗,大概應(yīng)該說,當(dāng)前的MessageQueue對頭的地址吧副砍,這個只是我的猜測不一定正確衔肢!
??還是來看看第二個return null的條件吧,從中可以知道只有當(dāng)mQuiting為true豁翎,才能返回 null角骤。從這個變量名,我們可以知道心剥,當(dāng)這個MessageQueue正在quit時邦尊,會返回null背桐。也就是說,當(dāng)MessageQueue要被回收時蝉揍,Looper會退出它的loop死循環(huán)链峭。其他的情況下,loop都是在進行死循環(huán)的又沾。
??但是弊仪,在哪一種情況下MessageQueue會被回收呢?我們發(fā)現(xiàn)MessageQueue的quit方法是默認(rèn)修飾符杖刷,也就是說撼短,只能在相同的包下,這個方法才能被調(diào)用挺勿!在我們的應(yīng)用程序代碼中是不能直接調(diào)用的,那哪里可以調(diào)用了quit方法呢喂柒?我們在Looper的quit方法里面找到了答案:
public void quit() {
mQueue.quit(false);
}
??我們知道不瓶,每一個Looper對象是被放在了一個ThreadLocal變量中,也就是說灾杰,在哪個線程調(diào)用Looper的quit方法蚊丐,就是quit哪個線程的MessageQueue,從而退出這個Looper的loop循環(huán)艳吠。
??例如:
private void sendMessage() {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.i("pby123", (msg.obj) + "");
}
};
Message message = Message.obtain();
message.obj = "我是Message";
handler.sendMessageDelayed(message, 2000);
Looper.loop();
Log.i("pby123", "123");
}
}).start();
}
??上面的代碼中麦备,我們在一個新的線程里面創(chuàng)建了一個Handler、Looper昭娩,然后使用Handler發(fā)送了一個消息凛篙,最后將調(diào)用Looper的loop方法,讓這個Looper活起來栏渺!然后我們來看看log:
??我們會發(fā)現(xiàn)呛梆,123這個log始終沒有打印出來,也就是說磕诊,根本沒有執(zhí)行到這一句話填物,從而推出此時Looper的loop方法還在執(zhí)行,當(dāng)然如果沒有消息的話霎终,應(yīng)該是被阻塞住了滞磺。這個現(xiàn)象間接的證明了,我們對源碼的理解莱褒!
??這里需要注意的是击困,在同一個線程中,代碼不能這樣寫:
new Thread(new Runnable() {
@Override
public void run() {
//這里將Looper的prepare方法注釋了
//表示當(dāng)前的線程Looper沒有被初始化
Looper.prepare();
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("pby123", (msg.obj) + "");
}
};
Looper.loop();
Message message = Message.obtain();
message.obj = "我是Message";
mHandler.sendMessageDelayed(message, 2000);
Log.i("pby123", "123");
}
}).start();
??這樣寫的話广凸,loop一直在執(zhí)行沛励,根本不能執(zhí)行到loop的代碼责语!
??從而得知:
??1.除主線程之外,所有線程的Looper的loop方法不會自動調(diào)用目派,盡管我們調(diào)用Handler發(fā)送消息的相關(guān)方法坤候。只有當(dāng)我們在線程中明確調(diào)用loop方法,才會進行l(wèi)oop的循環(huán)企蹭。
??2.loop方法是一個死循環(huán)白筹,它不斷的從MessageQueue里面去拿Message。只有當(dāng)取出的Message為null時谅摄,loop的死循環(huán)才能被結(jié)束徒河。
??2.在MessageQueue的next方法里面,如果取出的Message的執(zhí)行時間大于當(dāng)前的時間的話送漠,當(dāng)前線程就會被阻塞相應(yīng)的差值時間顽照。但是如果當(dāng)前的MessageQueue為空的話,那么就會當(dāng)前的線程就會被無限期的阻塞闽寡。
??3.如果當(dāng)前的MessageQueue被調(diào)用了quit方法代兵,Looper的loop方法從MessageQueue里面取出的是null,從而導(dǎo)致Looper的loop方法執(zhí)行完成爷狈。
??4.MessageQueue的quit方法不能被直接調(diào)用植影,而是通過Handler的quit來間接實現(xiàn)的!
??到這里涎永,我們可以知道思币,為什么loop方法不會導(dǎo)致ANR。這是因為loop不是一直執(zhí)行羡微,而是當(dāng)前MessageQueue里面有Message時谷饿,才會不斷的去獲取Message;如果MessageQueue是空的話妈倔,線程會被阻塞各墨,只有當(dāng)消息的時候才會被喚醒,loop方法才算是又活起來了启涯!
(2).enqueueMessage方法
??說實話贬堵,next方法真的不是很好的理解,特別是結(jié)合了Handler结洼、Looper之后!但是黎做,我們最終還是對next有了一個大概的理解,我可不敢說深入的理解松忍,哈哈蒸殿!因為自己本來就是一個菜雞!
??對next方法有了一個大概的理解,那么理解enqueueMessage方法應(yīng)該不是很難的宏所!先來看看enqueueMessage方法的代碼:
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (mQuitting) {
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
??上面的代碼中酥艳,我刪除了部分我認(rèn)為不重要的代碼。這個方法非常好理解爬骤,首先判斷這個MessageQueue是否被標(biāo)記為回收充石。如果被回收了的話,返回false霞玄,表示插入新的Message失斨枇濉;其次如果沒有被回收坷剧,那么插入將Message插入到正確的位置惰爬,這個正確的位置表示的是,按照Message的執(zhí)行時間從小到大排序惫企,時間最小的撕瞧,放在隊頭!
??我覺得這里最重要的一步是這個:
if (needWake) {
nativeWake(mPtr);
}
??如果當(dāng)前需要喚醒的話狞尔,就去喚醒丛版!
5.總結(jié)
??說實話,現(xiàn)在這篇文章寫得差不多了沪么,該寫一個總結(jié),將理解到的知識概括一下锌半!
??1.在創(chuàng)建Handler之前禽车,必須將當(dāng)前線程的Looper線程創(chuàng)建完畢,也就是Looper的preapre方法必須放在Handler創(chuàng)建之前刊殉。之所以在線程中不用創(chuàng)建Looper殉摔,那是因為Google爸爸在ActivityThread的main方法里面已經(jīng)給我們創(chuàng)建好了!
??2.Looper不會自動調(diào)用记焊,必須在本線程中的最后一行代碼來調(diào)用逸月!因為一旦loop方法執(zhí)行完畢,表示當(dāng)前的線程即將執(zhí)行完畢遍膜。這個可以參考ActivityThread的main方法代碼碗硬!
??3.在調(diào)用Handler的發(fā)送Message的相關(guān)方法時,最終會將該Message放入與該線程綁定的MessageQueue里面瓢颅。在放入Message恩尾,需要注意的是,Message本身帶了when屬性挽懦,這個表示Message時間翰意,MessageQueue內(nèi)部使用的這個When來排的序。
??4.在Looper的loop方法中,會不斷的調(diào)用MessageQueue的next來獲取一個Message冀偶。在獲取Message時醒第,要分為兩種情況:1.Message不為null,那么就該Message分發(fā)到Message屬于的那個Handler里面去消耗进鸠;2.如果為null的話稠曼,表示當(dāng)前的MessageQueue已經(jīng)被調(diào)用quit方法了,loop方法就會結(jié)束堤如,整個線程就會結(jié)束蒲列!
??5.在MessageQueue的next方法里面,如果獲取的Message執(zhí)行時間大于當(dāng)前的時間搀罢,表示還沒有達到該Message的執(zhí)行時機蝗岖,于是讓當(dāng)前的線程阻塞相應(yīng)的時間;如果獲取的Message符合執(zhí)行時機的話榔至,那么立即返回抵赢;如果當(dāng)前的MessageQueue為空了,立即阻塞當(dāng)前的線程唧取!
??最后铅鲤,在結(jié)合的知識來解釋一下,為什么我們在一個子線程中發(fā)送一個Message枫弟,主線程會被接受得到邢享。
??首先,我們我們使用的是在主線程中創(chuàng)建Handler來發(fā)送Message淡诗,雖然說是在子線程發(fā)送Message骇塘,但是Message有一個target用來記錄發(fā)送它的Handler。同時用于Handler在創(chuàng)建的時候韩容,會持有當(dāng)前線程的Looper和MessageQueue款违,所以這個Message最終還是發(fā)送了主線程的MessageQueue中。而在ActivityThread的main方法里面群凶,主線程的Looper在不斷的loop插爹,所以不斷的從自己線程的MessageQueue取Message來消耗,Message被獲取之后请梢,然后交給了target的handlerMessage方法去消耗赠尾,這個target就是在主線程我們自己創(chuàng)建的Handler。最終毅弧,我們就能知道為什么在子線程里面發(fā)送的Message能夠到達主線程中的Handler