Handler
一滴须、成員與構(gòu)造函數(shù)
Handler有一個(gè)靜態(tài)成員值得注意:
private static Handler MAIN_THREAD_HANDLER = null舌狗;
不出所料,有一個(gè)方法與其搭配:
public static Handler getMain() {
if (MAIN_THREAD_HANDLER == null) {
MAIN_THREAD_HANDLER = new Handler(Looper.getMainLooper());
}
return MAIN_THREAD_HANDLER;
}
這里直接將其Looper設(shè)置為了MainLooper
構(gòu)造函數(shù)
Handler的構(gòu)造函數(shù)之間有回調(diào)關(guān)系扔水,最終都會(huì)分別調(diào)用兩種構(gòu)造函數(shù):
public Handler(Callback callback, boolean async) {
........
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
所以如果Looper對(duì)象不傳遞的話痛侍,Handler會(huì)自己調(diào)用Looper.muLooper()獲取Handler所在線程綁定的Looper對(duì)象。
二魔市、Message的分發(fā)
Handler通過dispatchMessage分發(fā)Message:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
可見主届,處理Message的優(yōu)先級(jí)為:
- Handler的handleCallback優(yōu)先級(jí)最高
- Handler的Callback回調(diào)第二優(yōu)先級(jí)
- Handler的handleMessage方法優(yōu)先級(jí)最低
三、Message的產(chǎn)生
Handler本身有很多生成Message的方法:
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
平時(shí)都說待德,推薦使用Message.obtain()和Handler對(duì)象的obtainMessage方法來創(chuàng)建Message對(duì)象岂膳,且二者等價(jià),原因就在于handler對(duì)象的obtainMessage方法都是調(diào)用的Message.obtain()系列
注意:在Message.obtain方法中會(huì)將其target屬性初始化為Handler本身
四磅网、Message消息的發(fā)送
Handler中有很多方法可以發(fā)送消息,主要分為兩種:post和send
1. Post系列方法
這種方法都是直接post一個(gè)Runnable對(duì)象筷屡,所以首先來看看如何將一個(gè)Runnable對(duì)象封裝成一個(gè)Message:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
private static Message getPostMessage(Runnable r, Object token) {
Message m = Message.obtain();
m.obj = token;
m.callback = r;
return m;
}
很簡(jiǎn)單涧偷,就直接創(chuàng)建一個(gè)Message對(duì)象簸喂,然后將Runnable設(shè)置為Message的callback屬性
Q:post生成的Message沒有將handler對(duì)象綁定到其target屬性中?
對(duì)于該情況下的target賦值發(fā)生在handler的enqueueMessage方法中
現(xiàn)在我們來看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);
}
public final boolean postDelayed(Runnable r, Object token, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}
public final boolean postAtFrontOfQueue(Runnable r)
{
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
可以看到燎潮,Post方法都是回調(diào)的sendMessage系列方法
2. 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);
}
可見喻鳄,大部分send方法都是調(diào)用的sendMessageDelayed方法,而sendMessageDelayed方法又是調(diào)用的sendMessageAtTime方法實(shí)現(xiàn)的确封,所以我們可以說除呵,Handler中所有發(fā)送Message的方法的實(shí)現(xiàn)都是sendMessageAtTime:
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);
}
而sendMessageAtTime的實(shí)現(xiàn)實(shí)際上就是回調(diào)了enqueueMessage:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最終我們搞清楚了,原來Handler中所有的回調(diào)爪喘,都最終歸于queue.enqueueMessage方法颜曾。
而Handler中還有一個(gè)比較奇葩的方法:
public final boolean sendMessageAtFrontOfQueue(Message msg) {
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, 0);
}
這個(gè)看起來和sendMessageXX沒啥不同啊,仔細(xì)發(fā)現(xiàn)秉剑,sendMessageDelayed在回調(diào)sendMessageAtTime的時(shí)候第二個(gè)參數(shù)做了手腳:
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
其在delayMillis基礎(chǔ)上加了SystemClock.uptimeMillis(這個(gè)意義是手機(jī)從開機(jī)到當(dāng)下的時(shí)間間隔)泛豪,所以其永遠(yuǎn)都是一個(gè)大于0的數(shù);而sendMessageAtFrontOfQueue則把第二個(gè)參數(shù)直接置為0侦鹏,實(shí)際上其效果等同于:sendMessageAtTime(msg, 0)
注意:1. MessageQueue在執(zhí)行enqueue的時(shí)候會(huì)根據(jù)這個(gè)msg.when進(jìn)行插入诡曙,所以該值越小越先被MessageQueue.next方法返回,從而越先被Handler處理略水。 2. 為什么選擇使用SystemClock.uptimeMillis而不是System.currentMillis是因?yàn)楫?dāng)我們手動(dòng)更改系統(tǒng)時(shí)間的時(shí)候會(huì)影響后者的值价卤,導(dǎo)致MessageQueue在入隊(duì)插入的時(shí)候邏輯混亂
3. 小結(jié)
粗略下來,感覺post和send實(shí)現(xiàn)都一樣渊涝,為啥要有兩種api發(fā)送Message慎璧,雖然實(shí)現(xiàn)機(jī)制都一樣,但是在處理的時(shí)候就不一樣了驶赏。還記得handler的dispatchMessage中處理消息的優(yōu)先級(jí)嗎炸卑?沒錯(cuò):post系列方法的getPostMessage中將post的參數(shù)Runnable賦值給了Message的callback,所以導(dǎo)致在處理Post的消息的時(shí)候是通過最高優(yōu)先級(jí)的handleCallback方法處理的煤傍;而send系列方法盖文,在通常情況下是通過handler的callback屬性或者h(yuǎn)andleMessage方法處理消息。
五蚯姆、經(jīng)典面試題
Q1:子線程中創(chuàng)建Handler中報(bào)錯(cuò)是為什么五续?
在Handler的構(gòu)造器中有這么一段:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
因?yàn)闆]有執(zhí)行Looper.prepare()導(dǎo)致Looper.mylooper返回為null
Q2:如何在子線程創(chuàng)建Looper?
Looper.prepare()
Q3:為什么能通過Handler實(shí)現(xiàn)線程切換龄恋?
純屬個(gè)人理解:Handler的MessageQueue來自于Looper中的MessageQueue疙驾,而在Message.obtain中,Message的target被賦值為了Handler對(duì)象郭毕;Looper在從MessageQueue中取出Message后通過message.target來處理消息它碎,所以消息的處理是在Looper所綁定的線程,而Handler可以在任意線程往MessageQueue中發(fā)送消息,所以實(shí)現(xiàn)了線程的切換
Q4:Handler處理消息發(fā)送在那個(gè)線程扳肛,是Handler所在的線程還是Looper傻挂?
Looper所在的線程
Q5:Looper和Handler一定要處于一個(gè)線程嗎?子線程中可以用MainLooper去創(chuàng)建Handler嗎挖息?
不一定位于一個(gè)線程金拒;子線程可以通過MainLooper創(chuàng)建Handler:實(shí)現(xiàn)有兩種:
Handler h = new MyHandler(Looper.getMainLooper());
Handler h = Handler.getMain(); // Handler的api
Q6:Handler的post/send()的原理
最終都是調(diào)用的sendMessageAtTime -> enqueueMessage -> mQueue.enqueue
Q7:Handler的dispatchMessage()分發(fā)消息的處理流程?
優(yōu)先級(jí)為:
- 如果msg.callback不為空套腹,調(diào)用handleCallback
- 如果mCallback屬性不為空绪抛,則調(diào)用mCallback.handleMessage
- 最后調(diào)用handleMessage方法
Q8:Handler為什么要提供Callback構(gòu)造方法?
正常使用Handler要使用一個(gè)子類繼承Handler电禀,然后再子類中實(shí)現(xiàn)handleMessage方法幢码,而使用Callback構(gòu)造函數(shù)則可以簡(jiǎn)化這一流程,一步到位鞭呕。
Looper
一蛤育、成員和構(gòu)造器
Looper的成員有三個(gè)需要關(guān)注:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final Thread mThread;
其中的sThreadLocal就是實(shí)現(xiàn)Looper和線程綁定的原因。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構(gòu)造器中直接初始化了mThread和mQueue
二葫松、prepare()瓦糕、myLooper()方法
該方法大家都比較熟悉:
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));
}
就是在當(dāng)前線程中初始化了Looper對(duì)象,并且把它保存到了ThreadLocal中
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myLooper()方法就是將Looper對(duì)象從ThreadLocal中取出來
三腋么、quit和quitSafely
二者的區(qū)別就在于:mQueue.quit參數(shù)是否為true
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
通過注釋我們知道第一個(gè)方法可能導(dǎo)致還有沒有接受到的Message咕娄,可能會(huì)產(chǎn)生意外情況,所以推薦我們使用第二個(gè)方法
四珊擂、loop方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; 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;
}
...
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
這里只保留了關(guān)鍵的代碼:
- 要想loop結(jié)束只有msg為null圣勒,而在MessageQueue中只有調(diào)用了quit方法才會(huì)導(dǎo)致其next方法返回null;而Looper的quit和quirSafely方法內(nèi)部就是回調(diào)了mQueue的quit方法摧扇,所以只有調(diào)用Looper的quit或者quitSafely方法才會(huì)終止loop()方法
- msg.target.dispatchMessage(msg)中圣贸,msg的target即為handler對(duì)象,所以這里就實(shí)現(xiàn)了跨線程
五扛稽、經(jīng)典面試題
Q1:如何開啟消息循環(huán)吁峻?
Looper.loop()
Q2:Looper兩個(gè)退出方法有何區(qū)別?
quit會(huì)導(dǎo)致還未接收到的Message無法處理
quitSafely不會(huì)
Q3:如何終止消息循環(huán)在张?
quitSafely
Q4:Looper的loop流程
- 獲取Looper和MessageQueue
- 無限for循環(huán)取出msg
- 執(zhí)行msg.target.dispatchMessage
Q5:MessageQueue的next什么情況下返回null
執(zhí)行了quit或者quitSafely
Q6:Android如何保證一個(gè)線程最多只能有一個(gè)Looper用含?
通過ThreadLocal保證只有一個(gè)Looper
MessageQueue
這里需要提一下其Message mMessages代表鏈表的表頭
我們這里只討論其enqueueMessage方法和next方法。
1. enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
帮匾。啄骇。。
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
可以發(fā)現(xiàn)瘟斜,這里就是按照when的大小缸夹,將Message插入到鏈表中
2. next方法
Message next() {
...
int nextPollTimeoutMillis = 0;
for (;;) {
...
//根據(jù)我們上面的分析痪寻,nextPollTimeMillis為0則不阻塞,也就是第一次循環(huán)不阻塞
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//這里判斷msg不為空但是target為空明未,而我們enqueueMessage的時(shí)候特意設(shè)置了target的
//所以這里的msg不是我們?cè)O(shè)置而是系統(tǒng)在初始化的時(shí)候設(shè)置的屏障槽华,這里不再詳解
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());
}
//這里是正常情況下的msg
if (msg != null) {
//未達(dá)到處理時(shí)間的,將會(huì)計(jì)算需要等待的時(shí)間趟妥,不超過整形的最大值
if (now < msg.when) {
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;
}
//這里是隊(duì)列中沒有消息
} else {
nextPollTimeoutMillis = -1;
}
//上面分別對(duì)消息隊(duì)列進(jìn)行判斷然后修改nextPollTimeoutMillis,而之前的分析可以看出這個(gè)值就是線程
//需要阻塞的時(shí)長(zhǎng)佣蓉,有未達(dá)到處理時(shí)間的消息則阻塞對(duì)應(yīng)時(shí)間披摄,沒有消息則一直阻塞直到被喚醒
...
}
...
}
}
next方法詳細(xì)分析:MessageQueue的next方法詳解