前言
Handler
是Android的消息機(jī)制,他能夠很輕松的在線程間傳遞數(shù)據(jù)。由于Android開發(fā)規(guī)范的限制,我們不能在主線程執(zhí)行耗時(shí)操作(如網(wǎng)絡(luò),IO操作等),不能在子線程更新UI,所以Handler
大部分用來在耗時(shí)操作與更新UI之間切換。這讓很多人誤以為Handler
就是用來更新UI的,其實(shí)這只是它的一小部分應(yīng)用。
開始
我相信大多數(shù)人對(duì)Handler
的用法已經(jīng)爛熟于心了,這篇文章不會(huì)去探討Handler
的使用,而是著重從源碼上分析Handler
的運(yùn)行機(jī)制。
想要了解Handler
的運(yùn)行機(jī)制,我們需要了解 MessageQueue
,Message
,Looper
這幾個(gè)類。
-
MessageQueue
的意思就是消息隊(duì)列,它存儲(chǔ)了我們需要用來處理的消息Message
。 -
Message
是消息類,內(nèi)部存在一個(gè)Bundle
對(duì)象和幾個(gè)public
字段存儲(chǔ)數(shù)據(jù),MessageQueue
作為一個(gè)消息隊(duì)列不能自己處理消息,所以需要用到Looper
。 -
Looper
是一個(gè)循環(huán)裝置,他負(fù)責(zé)從不斷從MessageQueue
里取出Message
,然后回調(diào)給Handler
的handleMessage
來執(zhí)行具體操作。 -
Handler
在這里面充當(dāng)?shù)慕巧袷且粋€(gè)輔助類,它讓我們不用關(guān)系MessageQueue
和Looper
的具體細(xì)節(jié),只需要關(guān)系如何發(fā)送消息和回調(diào)的處理就行了。
上面講了幾個(gè)關(guān)鍵類在Handler
運(yùn)行機(jī)制中的職責(zé),相對(duì)大家對(duì)Handler機(jī)制有個(gè)粗略的了解。
我相信各位看官在閱讀這篇文章前都是帶著問題的,我們將通過問題來解答大家的疑惑。
分析
Looper
在分析Looper
之前,我們還需要知道ThreadLocal
這個(gè)類,如果對(duì)ThreadLocal
還不太了解,可以去看我的另一篇文章《ThreadLocal詳解》。
Looper是如何創(chuàng)建?
Handler
執(zhí)行的線程和它持有的Looper
有關(guān)。每個(gè)Thread
都可以創(chuàng)建唯一的Looper對(duì)象。
//為當(dāng)前線程創(chuàng)建Looper對(duì)象的方法。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//使用ThreadLocal來存儲(chǔ)當(dāng)前線程的Looper對(duì)象,這保證了每個(gè)線程有且僅有一個(gè)Looper對(duì)象。
//這里做了非空判斷,所以在同一個(gè)線程prepare方法是不允許被調(diào)用兩次的
//第一次創(chuàng)建好的Looper對(duì)象不會(huì)被覆蓋,它是唯一的。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
那么主線程的Looper
對(duì)象是怎么創(chuàng)建的呢?
public static void prepareMainLooper() {
//其實(shí)主線程創(chuàng)建Looper和其他線程沒有區(qū)別,也是調(diào)用prepare()。
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
//但是Looper用sMainLooper這個(gè)靜態(tài)變量將主線程的Looper對(duì)象存儲(chǔ)了起來
//可以通過getMainLooper()獲取,存儲(chǔ)MainLooper其實(shí)非常有作用,下面會(huì)講到。
sMainLooper = myLooper();
}
}
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
Looper是如何從MessageQueue取出消息并分發(fā)的?
Looper分發(fā)消息的主要邏輯在loop方法里
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
//保證當(dāng)前線程必須有Looper對(duì)象,如果沒有則拋出異常,調(diào)用Looper.loop()之前應(yīng)該先調(diào)用Looper.prepare().
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//Looper需要不斷從MessageQueue中取出消息,所以它持有MessageQueue對(duì)象
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();
for (;;) {
//這里開始執(zhí)行死循環(huán),queue通過調(diào)用next方法來取出下一個(gè)消息。
//很多人很疑惑死循環(huán)不會(huì)相當(dāng)耗費(fèi)性能嗎,如果沒有那么多消息怎么辦?
//其實(shí)當(dāng)沒有消息的時(shí)候,next方法會(huì)阻塞在這里,不會(huì)往下執(zhí)行了,性能問題不存在。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
//這里滿足了死循環(huán)跳出的條件,即取出的消息為null
//沒有消息next不是會(huì)阻塞嗎,怎么會(huì)返回null呢?
//其實(shí)只有MessageQueue停止的時(shí)候(調(diào)用quit方法),才會(huì)返回null
//MessageQueue停止后,調(diào)用next返回null,且不再接受新消息,下面還有詳細(xì)介紹。
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//這里的msg.target是Handler對(duì)象,分發(fā)消息到Handler去執(zhí)行。
//有人問主線程可以創(chuàng)建這么多Handler,怎么保證這個(gè)Handler發(fā)送的消息不會(huì)跑到其它Handler去執(zhí)行呢?
//那是因?yàn)樵诎l(fā)送Message時(shí),他會(huì)綁定發(fā)送的Handler,在此處分發(fā)消息時(shí),也只會(huì)回調(diào)發(fā)送該條消息的Handler。
//那么分發(fā)消息具體在哪個(gè)線程執(zhí)行呢?
//我覺得這個(gè)不該問,那當(dāng)然是當(dāng)前方法在哪個(gè)線程調(diào)用就在哪個(gè)線程執(zhí)行啦。
msg.target.dispatchMessage(msg);
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);
}
//這里對(duì)Message對(duì)象進(jìn)行回收,會(huì)清空所有之前Message設(shè)置的數(shù)據(jù)。
//正是因?yàn)镸essage有回收機(jī)制,我們?cè)趧?chuàng)建消息的時(shí)候應(yīng)該優(yōu)先選擇Message.obtain().
//如果發(fā)送的消息足夠多,Message緩存的Message對(duì)象不夠了,obtain內(nèi)部會(huì)調(diào)用new Message()創(chuàng)建一個(gè)新的對(duì)象。
msg.recycleUnchecked();
}
}
Looper 分發(fā)的消息在哪個(gè)線程執(zhí)行?
先給大家展示一段Looper
文檔上的示例代碼
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //創(chuàng)建LooperThread的Looper對(duì)象
mHandler = new Handler() {
public void handleMessage(Message msg) {
//處理發(fā)送過來的消息
}
};
Looper.loop(); //開始循環(huán)消息隊(duì)列
}
}
上面這段代碼相信很多人都寫過,這是一段在子線程創(chuàng)建Handler的案例,其中handleMessage
所執(zhí)行的線程為LooperThread
,因?yàn)?code>Looper.loop()執(zhí)行在LooperThread
的run
方法里??梢栽谄渌€程通過mHandler
發(fā)送消息到LooperThread
如果不調(diào)用Looper.prepare()
直接new Handler()
會(huì)怎么樣呢?
我們可以查看Handler
的源碼看看無參構(gòu)造是如何運(yùn)行的
public Handler() {
//調(diào)用兩參構(gòu)造
this(null, false);
}
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());
}
}
//獲取當(dāng)前線程的Looper,如果不創(chuàng)建Looper會(huì)拋出異常。
//主線程我也沒看到有調(diào)用Looper.prepare()啊,怎么在主線程不會(huì)拋異常呢?這個(gè)看下一個(gè)問題。
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;
}
主線程的Looper對(duì)象在哪里創(chuàng)建的?
從上一個(gè)問題可以看出如果不調(diào)用Looper.prepare()
直接new Handler()
就會(huì)拋出異常`
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");`
那么主線程的Looper
在哪里創(chuàng)建的呢?首先它是創(chuàng)建了的,因?yàn)?code>Looper.getMainLooper() != null,其實(shí)MainLooper
創(chuàng)建的時(shí)間比我們想象的早,它在ActivityThread
類里面,ActivityThread
是Android
的啟動(dòng)類,main
方法就在里面(如果有人問你Android有沒有main方法,你應(yīng)該知道怎么回答了吧),而MainLooper
就是在main
方法里面創(chuàng)建的。
上代碼:
//android.app.ActivityThread
public final class ActivityThread {
...
public static void main(String[] args) {
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());
Security.addProvider(new AndroidKeyStoreProvider());
// 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>");
//注意這里,這里創(chuàng)建了主線程的Looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
//開啟消息循環(huán)
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
MainLooper可以用來做什么
判斷當(dāng)前線程是否為主線程
因?yàn)長(zhǎng)ooper是在某一線程唯一的,那么可以在么做。如果
public static boolean isMainThread() {
//如果當(dāng)前線程的Looper和MainLooper是同一個(gè)對(duì)象,那么可以認(rèn)為當(dāng)前線程是主線程
return Looper.myLooper() == Looper.getMainLooper() ;
}
但是也有人說下面這樣也可以
public static boolean isMainThread() {
//這個(gè)方法其實(shí)是不準(zhǔn)確的,線程的名稱是可以隨便更改的。
return Thread.currentThread().getName().equals("main");
}
所以用Looper
來判斷主線程是很好的做法
創(chuàng)建運(yùn)行在主線程的Handler
Handler
除了有無參構(gòu)造,還有一個(gè)可以傳入Looper
的構(gòu)造。通過指定Looper
,可以在任意地方創(chuàng)建運(yùn)行在主線程的Handler
class WorkThread extends Thread{
private Handler mHandler;
@Override
public void run() {
super.run();
mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//運(yùn)行在主線程
}
};
mHandler.sendEmptyMessage(0);
}
}
Looper的quit方法和quitSafely方法有什么區(qū)別
下面是Looper
兩個(gè)方法的源碼
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
可以看出實(shí)際上是調(diào)用的MessageQueue
的quit
方法
下面是MessageQueue
的源碼
//android.os.MessageQueue
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//如果調(diào)用的是quitSafely運(yùn)行removeAllFutureMessagesLocked,否則removeAllMessagesLocked。
if (safe) {
//該方法只會(huì)清空MessageQueue消息池中所有的延遲消息,
//并將消息池中所有的非延遲消息派發(fā)出去讓Handler去處理,
//quitSafely相比于quit方法安全之處在于清空消息之前會(huì)派發(fā)所有的非延遲消息。
removeAllFutureMessagesLocked();
} else {
//該方法的作用是把MessageQueue消息池中所有的消息全部清空,
//無論是延遲消息(延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發(fā)送的需要延遲執(zhí)行的消息)還是非延遲消息。
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
無論是調(diào)用了quit
方法還是quitSafely
方法,MessageQueue
將不再接收新的Message
,此時(shí)消息循環(huán)就結(jié)束,MessageQueued
的next
方法將返回null
,結(jié)束loop()
的死循環(huán).這時(shí)候再通過Handler
調(diào)用sendMessage
或post
等方法發(fā)送消息時(shí)均返回false
,表示消息沒有成功放入消息隊(duì)列MessageQueue
中,因?yàn)橄㈥?duì)列已經(jīng)退出了。
Message
Message.obtain()和new Message()如何選擇
Message
提供了obtain
等多個(gè)重載的方法來創(chuàng)建Message
對(duì)象,那么這種方式和直接new
該如何選擇。下面看看obtain
的代碼。
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message(); //只有當(dāng)從對(duì)象池里取不出Message才去new
}
void recycleUnchecked() {
//清除所有使用過的痕跡
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
//回收到對(duì)象池
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
從上面代碼可以看出,通過obtain
方法是從對(duì)象池取,而new
是創(chuàng)建了一個(gè)新的對(duì)象。我們應(yīng)該使用obtain
來創(chuàng)建Message
對(duì)象,每次使用完后都會(huì)自動(dòng)進(jìn)行回收,節(jié)省內(nèi)存。
......