Handler是Android消息通訊當(dāng)中最常用的方式之一。
本篇小文將會(huì)從Handler的源碼角度去淺析Handler。
總結(jié)
因?yàn)镠andler這個(gè)東西其實(shí)大家都會(huì)用,源碼也多多少少地了解過,所以直接將最關(guān)鍵的話,寫到前面,對(duì)源碼感興趣的看官可以在看完總結(jié)后再往下瀏覽源碼:
- 創(chuàng)建Handler
Handler handler = new Handler();
在主線程當(dāng)中,可以直接進(jìn)行Handler的創(chuàng)建。如果是在子線程當(dāng)中,在創(chuàng)建之前必須先初始化Looper,否則會(huì)RuntimeException:
Looper.prepare();
Handler handler = new Handler();
handler.sendEmptyMessage(8);
Looper.loop();
在初始化Looper的同時(shí),一定要調(diào)用Looper.loop()
來啟動(dòng)循環(huán),否則Handler仍然無法正常接收。
并且因?yàn)?code>Looper.loop()有死循環(huán)的存在,Looper.loop()
之后的代碼將無法執(zhí)行,所以需要將Looper.loop()
放在代碼的最后,這點(diǎn)在下面源碼解析會(huì)解釋。
并且Looper在每個(gè)線程只能存在一個(gè),如果再去手動(dòng)創(chuàng)建Looper也會(huì)拋出RuntimeException。
- 發(fā)送Handler
Handler的發(fā)送有很多方法,包括發(fā)送延時(shí)Handler、即時(shí)Handler、MessageHandler等等,但是查看這些發(fā)送方法的源碼,就會(huì)發(fā)現(xiàn)這些發(fā)送Message的方法最終都會(huì)調(diào)用MessageQueue.enqueueMessage()
方法。
這個(gè)方法其實(shí)就是將我們發(fā)送的Message入隊(duì)到MessageQueue隊(duì)列中,這樣,我們的消息就已經(jīng)發(fā)送成功,等待執(zhí)行了。 - 取出Handler
在Looper.prepare()
的同時(shí),總會(huì)執(zhí)行looper.loop()
語句與之對(duì)應(yīng)。
查看loop()
源碼會(huì)發(fā)現(xiàn),這個(gè)方法中有一個(gè)for(;;)的死循環(huán),會(huì)無限執(zhí)行MessageQueue.next()
,而MessageQueue
就是我們上一步將Meesage入隊(duì)的對(duì)象。
也就是說在創(chuàng)建Looper時(shí),就會(huì)啟動(dòng)MessageQueue
的無限遍歷。如果MessageQueue
為空,Looper.loop()
就會(huì)進(jìn)入休眠,直到再有Message插入到MessageQueue
中。
如果取到Message則會(huì)調(diào)用message.target.dispatchMessage(),將消息分發(fā)給對(duì)應(yīng)的Handler。 - 如何在
loop()
休眠之后喚醒loop()
?
在Meesage入隊(duì)的時(shí)候,也就是執(zhí)行MessageQueue.enqueueMessage()
方法時(shí),enqueueMessage()
有一個(gè)nativeWeak()
的native方法,如果有消息進(jìn)入,并且Looper是休眠狀態(tài),則會(huì)執(zhí)行該方法喚醒Looper:
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
- 整體流程
先調(diào)用Looper.prepare()
創(chuàng)建Looper,在創(chuàng)建的同時(shí)會(huì)自動(dòng)調(diào)用Looper.loop()
執(zhí)行死循環(huán)loop()。注意Looper.loop()
一定放到代碼的最后一行。
死循環(huán)中會(huì)執(zhí)行MessageQueue.next()
方法去取出隊(duì)列中的消息,當(dāng)消息為空時(shí),MessageQueue.next()
方法中會(huì)執(zhí)行nativePollOnce()
的native方法休眠Looper.loop()
死循環(huán)。當(dāng)有新的消息插入到MessageQueue
中,也就是調(diào)用MessageQueue.enqueueMessage()
方法,這個(gè)方法當(dāng)中會(huì)判斷Looper是否是休眠狀態(tài),如果是休眠狀態(tài)會(huì)執(zhí)行nativeWeak()
的native方法來喚醒Looper()。
Handler的使用
- 主線程
在主線程
當(dāng)中,Handler可以作為一個(gè)成員變量直接進(jìn)行創(chuàng)建://注意,Handler屬于android.os包 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { System.out.println("主線程的Handler"); } };
接著我們?cè)囍l(fā)送Handler:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//發(fā)送一個(gè)空Handler,what為888,延遲3秒到達(dá)
handler.sendEmptyMessageDelayed(888,3000);
}
發(fā)送Handler有很多方法:發(fā)送空Message、發(fā)送延遲的空Message、發(fā)送Message、發(fā)送延遲的Message等:
接著我們運(yùn)行項(xiàng)目,就會(huì)發(fā)現(xiàn)3s后控制臺(tái)有Log的打印。
- 子線程
在子線程
中創(chuàng)建Handler的流程,和在主線程
基本一致,只是多了一步而已,但這一步非常關(guān)鍵。
我們先按照主線程
的步驟,看看會(huì)有什么問題。
在子線程
中創(chuàng)建Handler:
new Thread(){
@Override
public void run() {
super.run();
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("子線程的Handler");
}
};
}
}.start();
接著我們運(yùn)行項(xiàng)目,會(huì)發(fā)現(xiàn)項(xiàng)目崩潰了:
java.lang.RuntimeException: Can''t create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:200)
at android.os.Handler.<init>(Handler.java:114)
at com.my.oo.MainActivity$2$1.<init>(MainActivity.java:0)
很明顯,錯(cuò)誤信息說的是在當(dāng)前線程中創(chuàng)建Handler失敗因?yàn)闆]有執(zhí)行Looper.prepare()。
那我們按照錯(cuò)誤原因,在創(chuàng)建Handler之前,加上Looper.prepare()
:
//子線程
new Thread(){
@Override
public void run() {
super.run();
//添加Looper.prepare();
Looper.prepare();
//之后再創(chuàng)建Handler
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("子線程的Handler");
}
};
//發(fā)送消息
handler.sendEmptyMessage(111);
}
}.start();
這次再運(yùn)行項(xiàng)目,我們就會(huì)發(fā)現(xiàn)項(xiàng)目正常運(yùn)行沒有問題。但是發(fā)送Handler仍然無法接收,那是因?yàn)槲覀儧]有啟動(dòng)Looper的遍歷:
//子線程
new Thread(){
@Override
public void run() {
super.run();
//添加Looper.prepare();
Looper.prepare();
//之后再創(chuàng)建Handler
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
System.out.println("子線程的Handler");
}
};
//發(fā)送消息
handler.sendEmptyMessage(111);
//啟動(dòng)Looper的遍歷功能
Looper.loop();
}
}.start();
這里一定要注意,Looper.loop()
必須放到代碼的最后。因?yàn)?code>Looper.loop()中有死循環(huán),會(huì)導(dǎo)致之后的代碼無法執(zhí)行。這里可以等到查看主線程
創(chuàng)建過程的源碼時(shí)證實(shí)。
- 使用總結(jié)
Handler的使用就是這么簡(jiǎn)單,要注意的就是子線程
當(dāng)中使用Handler時(shí),一定要先調(diào)用Looper.prepare()
,最后調(diào)用Looper.loop()
,否則項(xiàng)目會(huì)崩潰或無法接收Handler。至于為什么會(huì)這樣,我們?cè)谠创a里面找原因。
源碼解析
- Handler創(chuàng)建
我們先看Handler的構(gòu)造方法:
public Handler(Callback callback, boolean async) {
//省略部分代碼
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)造方法當(dāng)中是去獲取了一個(gè)叫做Looper
的類對(duì)象,如果該對(duì)象為空,就會(huì)拋出剛才我們上面發(fā)生的異常。所以我們需要在創(chuàng)建Handler之前,一定要先執(zhí)行Looper.prepare()
。
那么問題來了,為什么主線程
就不需要執(zhí)行Looper.prepare()
就可以直接創(chuàng)建Handler呢?
我們可以隨意根據(jù)代碼猜測(cè)一下:
這里Handler的構(gòu)造方法的代碼已經(jīng)很明顯了,Looper
類是必要的,那么主線程
可以成功創(chuàng)建Handler,是不是就代表著主線程的Looper
不為空呢?
是不是主線程在初始化的時(shí)候
Looper
也跟著初始化了呢!?帶著看破一切(瞎猜)的思路,我們來看主線程
的初始化源碼。經(jīng)過了長(zhǎng)時(shí)間的Google,我們知道了主線程類叫做:
ActivityThread
。該類當(dāng)中有main方法:
public static void main(String[] args) {
//....省略部分代碼
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");
}
我們很自然(驚奇)地發(fā)現(xiàn),在主線程創(chuàng)建的過程中,果然(真的)有與Looper
類相關(guān)的內(nèi)容。
這里有重點(diǎn)(敲黑板):之前說的Looper.loop()后的代碼不會(huì)執(zhí)行,這里得到了證實(shí):
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
下面異常的意思是:主線程的Looper意外退出。
也就是當(dāng)Looper.loop()執(zhí)行失敗的意思,但是當(dāng)Looper.loop()執(zhí)行成功時(shí),是不會(huì)執(zhí)行下面的代碼的!因?yàn)?strong>Looper.loop()
必須放到方法的最后,否則會(huì)導(dǎo)致后面的代碼無法執(zhí)行。
好的,接著往下看,點(diǎn)進(jìn)prepareMainLooper()
會(huì)發(fā)現(xiàn),其實(shí)內(nèi)部就是調(diào)用了prepare()
:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
所以到現(xiàn)在,我們解決了我們的第一個(gè)問題:為什么主線程當(dāng)中不需要執(zhí)行Looper.prepare()
。
接著,我們?nèi)g覽Looper.prepare()
:
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//new Looper()并設(shè)置給當(dāng)前線程
sThreadLocal.set(new Looper(quitAllowed));
}
沒什么東西,前面對(duì)線程當(dāng)中的Looper進(jìn)行了判空,如果不為空則會(huì)拋出RuntimeEception
。
這也就是說,每個(gè)線程當(dāng)中只能有一個(gè)Looper,當(dāng)你嘗試去創(chuàng)建第二時(shí),就會(huì)發(fā)生異常,所以Looper.prepare()
每個(gè)線程中只能調(diào)用一次。
后面則new了Looper并且設(shè)置給當(dāng)前線程。
new Looper()
中初始化了MessageQueue
:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
到這里,Handler的創(chuàng)建就完成了!
- Handler發(fā)送Meesage
Handler的發(fā)送方法有很多,包括發(fā)送延時(shí)Handler、及時(shí)Handler、空Hanlder等等。
查看源碼會(huì)發(fā)現(xiàn),所有發(fā)送方法最后調(diào)用的都是同一個(gè)方法:MessageQueue
的enqueueMessage()
。
有的看官就會(huì)問:Handler中怎么會(huì)有MeesageQueue
?
這個(gè)在上面new Looper()
的源碼中已經(jīng)體現(xiàn)了:
new Handler()
中new Looper()
,
new Looper()
中new MessageQueue()
。
所以其實(shí)初始化Handler的同時(shí),Looper
和MeesageQueue
都已經(jīng)初始化完成了。
下面我們來看消息入隊(duì)方法MessageQueue.enqueueMessage()
的全部源碼:
boolean enqueueMessage(Message msg, long when) {
//Meesage是否可用
//這里的msg.target指的就是發(fā)送該Message的Handler
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) {
//判斷是否調(diào)用了quit()方法,即取消信息
//如果調(diào)用了,則其實(shí)Handler的Looper已經(jīng)銷毀,無法發(fā)送消息
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;
}
//將消息添加到MessageQueue的具體操作
//每來一個(gè)新的消息,就會(huì)按照延遲時(shí)間的先后重新進(jìn)行排序
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 {
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;
}
//如果Looper.loop()是休眠狀態(tài)
//則調(diào)用native方法喚醒loop()
//---重點(diǎn)---Looper的喚醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
將Message入隊(duì)到MeesageQueue
的核心代碼,就是這些。
根據(jù)注釋也基本能理解該方法的作用。
- 取出Message
取出Meesage想必大家都知道在哪里取出:Looper.loop()
:
public static void loop() {
//Looper的判空
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
//取出Message,死循環(huán)
for (;;) {
//取出Meesage的核心代碼
//在當(dāng)next()返回為空時(shí),next()中會(huì)休眠loop()
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Looper.loop()
的核心就是取出Message,而取出Message的核心就是MeesageQueue.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();
}
//喚醒Looper.loop()的native方法
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
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
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.M
} 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 hand
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(pendingIdleHandlerCo
}
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;
}
}
取出Meesage的代碼有些多,大部分都是一些優(yōu)化邏輯:next() 方法還做了其他一些事情,這些其它事情是為了提高系統(tǒng)效果,利用消息隊(duì)列在空閑時(shí)通過 idle handler 做一些事情,比如 gc 等等。
結(jié)語
到這里Handler源碼的淺析就結(jié)束了,總結(jié)在最上方,建議各位看官再去看一下總結(jié)加深印象。