首先來一個整體的印象:
我們將這四個對象看做一條流水線的工作流程贡避,Handler(工人),MessageQueue(傳送帶)予弧,Message(待處理的貨物)刮吧,Looper(傳送帶的發(fā)動機)
工作流程: 工人(Handler) 將 貨物(Message) 放到 傳送帶(MessageQueue)上,發(fā)動機(Looper) 帶動整個傳送帶往前走掖蛤,工人將傳送帶上的貨物取下來打包處理杀捻。
流水線要能正常的工作,工人(Handler)首先要有一條傳送帶蚓庭,傳送帶要有發(fā)動機致讥,要傳送貨物當然還得有貨物(Message),當然發(fā)動機還得啟動(Looper.loop())器赞。
這個對應(yīng)了Handler中的幾個屬性垢袱,來看看Handler的屬性組成:
final Looper mLooper;//發(fā)動機
final MessageQueue mQueue;//傳送帶
final Callback mCallback;//
final boolean mAsynchronous;
所以一個正常的Handler要正常工作,所必需的的三大要素:
1.構(gòu)造一個MessageQueue港柜,這個消息隊列用來存儲handler的sendMessage(Message msg)傳過來的Message请契。
2.構(gòu)造發(fā)動機唯一的Looper對象(每一條流水線只能有一臺發(fā)動機),這個looper是整個消息傳遞機制動力的來源
3.所有工作完成之后,要啟動發(fā)動機爽锥,即Looper要啟動涌韩,注意looper一經(jīng)啟動就意味著所有的準備工作已經(jīng)完成,也就是說所有的準備工作必須在looper啟動之前救恨,因為looper是個死循環(huán)贸辈,在這個循環(huán)啟動之后的代碼如果執(zhí)行了,說明整個系統(tǒng)已經(jīng)出錯了或者退出了肠槽。這也就是一般Looper.looper()是寫在方法最后一句的原因擎淤。
那么問題來了:
1.有人會問,既然looper是個死循環(huán)秸仙,它不會一直運行導(dǎo)致占用太多的系統(tǒng)資源導(dǎo)致系統(tǒng)內(nèi)存溢出崩潰嗎嘴拢?
要回答這個問題首先要知道,線程中默認是沒有Looer這個東西的寂纪,但是如果你需要使用 Handler席吴,那么這個東西還會被創(chuàng)建,那么我們先來看看Looper是在哪里被創(chuàng)建的捞蛋,又是如何與線程產(chǎn)生聯(lián)系的孝冒。那么首先來看Handler的創(chuàng)建,不多說上源碼:
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();//這里是構(gòu)造我們的發(fā)動機
if (mLooper == null) {
throw new RuntimeException(//這里給出了提示拟杉,如果沒有調(diào)用Looper.prepare()方法就運行Handler的話會報錯
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//構(gòu)造發(fā)動機的同時也構(gòu)造了傳送帶
mCallback = callback;
mAsynchronous = async;
}
Handler有三個構(gòu)造函數(shù)胆建,但最后會調(diào)用上面的揩慕,代碼很簡單,就是簡單的構(gòu)造所需要的組件,下面來看 Looper.myLooper()方法:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
..............................
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
是不是覺得有點熟悉了雄家,對钞翔,ThreadLocal蜕提!前面講了ThreadLocal的set與get方法可以設(shè)置變量為線程的私有變量全庸。那么我們?nèi)タ纯磗ThreadLocal 的set方法在哪調(diào)用:
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));//這里簡單的new了一個looper對象
}
..........................
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//創(chuàng)建了messageQueue對象
mThread = Thread.currentThread();
}
這也就是我們前面提到的在創(chuàng)建Handler之前不調(diào)用Looper.prepare()方法會報錯的原因。因為sThreadLocal.get()拿到的是空值默色!這樣looper與當前線程就形成了邏輯上的一對一的關(guān)系
了解了looper的創(chuàng)建我們來看看Android的主線程球凰,Android應(yīng)用的入口在ActivityThread類,我們來看看它的main函數(shù):
public static void main(String[] args) {
..................................
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();//準備主循環(huán)
ActivityThread thread = new ActivityThread();//創(chuàng)建ActivityThread對象腿宰,其中會創(chuàng)建一個H 對象呕诉,其實也就是我們說的handler對象
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...........................
Looper.loop();//啟動主循環(huán)
throw new RuntimeException("Main thread loop unexpectedly exited");
}
final H mH = new H();//這是activityThread中創(chuàng)建的H對象
從上面我們可以知道,looper的創(chuàng)建是在 Handler 創(chuàng)建之前的酗失,當一切準備就緒之后义钉,調(diào)用Looper.loop()應(yīng)用程序就進入了死循環(huán)。
回到上面的問題规肴,既然主線程進入了死循環(huán)捶闸,那么這個死循環(huán)會不會導(dǎo)致應(yīng)用卡死夜畴,即使不會卡死會不會過度的耗費系統(tǒng)資源(CPU)?
答案大家都知道 是肯定不會删壮,但是是為什么呢贪绘?
就我個人的理解哈,設(shè)計成死循環(huán)的目的央碟,是讓主線程能一直運行下去(除非用戶自己退出)税灌,我們也不想我們的應(yīng)用程序運行一段時間就自動退出吧?出于這個考慮亿虽,死循環(huán)是最好的選擇菱涤。其實binder內(nèi)部也是采用的死循環(huán)。再回過頭來看Looper.looper方法:
public static void loop() {
final Looper me = myLooper();//獲取當前線程的looper
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();
for (;;) {//死循環(huán) 一個個去取出消息
Message msg = queue.next(); // might block不斷去取messageQueue中的消息洛勉,這個地方有可能阻塞
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的工作過程,首先拿到當前線程的looper收毫,然后通過這個looper拿到當前的messageQueue攻走,然后進入死循環(huán),不斷的去取messageQueue中的消息此再,即調(diào)用messageQueue中的next方法昔搂,這個方法有可能會阻塞,也就是主線程休眠输拇。如果next方法取到的值為 null 摘符,那么表示消息隊列在退出了,也就是整個線程在退出了
這里的next方法的結(jié)果只會有三種狀態(tài)
1.返回正常值
2.阻塞淳附,表示隊列中沒有消息议慰,主線程進入休眠
3.返回null蠢古,表示線程在退出
上面說了奴曙,在隊列中沒有消息時(實際判斷沒這么簡單),主線程會進入休眠草讶,因此也不至于過度的耗費系統(tǒng)資源洽糟。
簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法里堕战,此時主線程會釋放CPU資源進入休眠狀態(tài)坤溃,直到下個消息到達或者有事務(wù)發(fā)生,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作嘱丢。所以說薪介,主線程大多數(shù)時候都是處于休眠狀態(tài),并不會消耗大量CPU資源越驻。
2.沒看見哪里有相關(guān)代碼為這個死循環(huán)準備了一個新線程去運轉(zhuǎn)汁政?
這個我們其實在最初接觸android的時候就知道道偷,當一個安卓應(yīng)用啟動的時候,记劈,每個App進程中至少會有兩個binder線程 ApplicationThread(簡稱AT)和ActivityManagerProxy(簡稱AMP)勺鸦,除了圖中畫的線程,其中還有很多線程目木,比如signal catcher線程等换途。
事實上,會在進入死循環(huán)之前便創(chuàng)建了新binder線程刽射,在代碼ActivityThread.main()中:
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);//便會創(chuàng)建一個Binder線程
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
thread.attach(false)军拟;便會創(chuàng)建一個Binder線程(具體是指ApplicationThread,Binder的服務(wù)端誓禁,用于接收系統(tǒng)服務(wù)AMS發(fā)送來的事件)吻谋,該Binder線程通過Handler將Message發(fā)送給主線程
3. Activity的生命周期是怎么實現(xiàn)在死循環(huán)體外能夠執(zhí)行起來的?
前面說到了ActivityThread中H對象现横,ActivityThread的內(nèi)部類H繼承于Handler漓拾,通過handler消息機制,簡單說Handler機制用于同一個進程的線程間通信戒祠,而Binder是用于進程中通信骇两!
Activity的生命周期都是依靠主線程的Looper.loop,當收到不同Message時則采用相應(yīng)措施:在H.handleMessage(msg)方法中姜盈,根據(jù)接收到不同的msg低千,執(zhí)行相應(yīng)的生命周期。 比如收到msg=H.LAUNCH_ACTIVITY馏颂,則調(diào)用ActivityThread.handleLaunchActivity()方法示血,最終會通過反射機制,創(chuàng)建Activity實例救拉,然后再執(zhí)行Activity.onCreate()等方法难审; 再比如收到msg=H.PAUSE_ACTIVITY,則調(diào)用ActivityThread.handlePauseActivity()方法亿絮,最終會執(zhí)行Activity.onPause()等方法告喊。 上述過程,我只挑核心邏輯講派昧,真正該過程遠比這復(fù)雜黔姜。
主線程的消息又是哪來的呢?當然是App進程中的其他線程通過Handler發(fā)送給主線程蒂萎,請看接下來的內(nèi)容:
結(jié)合圖說說Activity生命周期秆吵,比如暫停Activity,流程如下:
1.線程1的AMS中調(diào)用線程2的ATP五慈;(由于同一個進程的線程間資源共享纳寂,可以相互直接調(diào)用实苞,但需要注意多線程并發(fā)問題)
2.線程2通過binder傳輸?shù)紸pp進程的線程4;
3.線程4通過handler消息機制烈疚,將暫停Activity的消息發(fā)送給主線程黔牵;
4.主線程在looper.loop()中循環(huán)遍歷消息,當收到暫停Activity的消息時爷肝,便將消息分發(fā)給ActivityThread.H.handleMessage()方法猾浦,再經(jīng)過方法的調(diào)用,最后便會調(diào)用到Activity.onPause()灯抛,當onPause()處理完后金赦,繼續(xù)循環(huán)loop下去。
總結(jié)來說就是: