一蛉艾、提出問(wèn)題
面試時(shí)常被問(wèn)到的問(wèn)題:
- 簡(jiǎn)述 Android 消息機(jī)制
- Android 中 Handler照棋,Looper,MessageQueue泳梆,Message 有什么關(guān)系鳖悠?
這倆問(wèn)題其實(shí)是一個(gè)問(wèn)題,其實(shí)只要搞清楚了 Handler优妙,Looper乘综,MessageQueue,Message 的作用和聯(lián)系套硼,就理解了 Android 的 Handler 消息機(jī)制卡辰。那么再具體一點(diǎn):
- 為什么在主線程可以直接使用 Handler?
- Looper 對(duì)象是如何綁定 MessageQueue 的邪意?
- MessageQueue 里的消息從哪里來(lái)九妈?Handler是如何往MessageQueue中插入消息的?
- Message 是如何綁定 Handler 的雾鬼?
- Handler 如何綁定 MessageQueue萌朱?
- 關(guān)于 handler,在任何地方 new handler 都是什么線程下策菜?
- Looper 循環(huán)拿到消息后怎么處理晶疼?
二、解決問(wèn)題
那么又憨,我們從主線程的消息機(jī)制開(kāi)始分析:
2.1 主線程 Looper 的創(chuàng)建和循環(huán)
Android 應(yīng)用程序的入口是 main 函數(shù)翠霍,主線程 Looper 的創(chuàng)建也是在這里完成的。
ActivityThread --> main() 函數(shù)
public static void main(){
// step1: 創(chuàng)建主線程Looper對(duì)象
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
// 綁定應(yīng)用進(jìn)程蠢莺,布爾標(biāo)記是否為系統(tǒng)進(jìn)程
thread.attach(false);
// 實(shí)例化主線程 Handler
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
// step2: 開(kāi)始循環(huán)
Loop.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper()
用來(lái)創(chuàng)建主線程的 Looper 對(duì)象壶运,接下來(lái)先看這個(gè)方法的實(shí)現(xiàn)。
2.1.1 創(chuàng)建主線程 Looper
Looper --> prepareMainLooper()
private static Looper sMainLooper; // guarded by Looper.class
public static void prepareMainLooper(){
// step1: 調(diào)用本類(lèi) prepare 方法
prepare(false);
// 線程同步浪秘,如果變量 sMainLooper 不為空拋出主線程 Looper 已經(jīng)創(chuàng)建
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// step2: 調(diào)用本類(lèi) myLooper 方法
sMainLooper = myLooper();
}
}
prepareMainLooper()
方法主要是使用 prepare(false)
創(chuàng)建當(dāng)前線程的 Looper 對(duì)象蒋情,再使用 myLooper()
方法來(lái)獲取當(dāng)前線程的 Looper 對(duì)象。
step1: Looper --> prepare()
// ThreadLocal 為每個(gè)線程保存單獨(dú)的變量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// Looper 類(lèi)的 MessageQueue 變量
final MessageQueue mQueue;
// quitAllowed 是否允許退出耸携,這里是主線程的 Looper 不可退出
private static void prepare(boolean quitAllowed) {
// 首先判定 Looper 是否存在
if(sThreadLocal.get() != null){
throw new RuntimeException("Only one Looper may be created per thread");
}
// 保存線程的副本變量
sThreadLoacal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepare()
方法中用 ThreadLocal 來(lái)保存主線程的 Looper 對(duì)象棵癣。ThreadLocal 可以看作是一個(gè)用來(lái)儲(chǔ)存數(shù)據(jù)的類(lèi),類(lèi)似 HashMap夺衍、ArrayList等集合類(lèi)狈谊,它存放著屬于當(dāng)前線程的變量。ThreadLocal 提供了 get/set 方法分別用來(lái)獲取和保存變量。
比如在主線程通過(guò)prepare()
方法來(lái)創(chuàng)建 Looper 對(duì)象河劝,并使用sThreadLoacal.set(new Looper(quitAllowed))
來(lái)保存主線程的 Looper 對(duì)象壁榕,那么在主線程調(diào)用myLooper()
(實(shí)際調(diào)用了sThreadLocal.get()
方法) 就是通過(guò) ThreadLocal 來(lái)獲取主線程的 Looper 對(duì)象。如果在子線程調(diào)用這些方法就是通過(guò) ThreadLocal 保存和獲取屬于子線程的 Looper 對(duì)象赎瞎。
更多關(guān)于 ThreadLocal 的原理:
問(wèn)題1:為什么在主線程可以直接使用 Handler牌里?
因?yàn)橹骶€程已經(jīng)創(chuàng)建了 Looper 對(duì)象并開(kāi)啟了消息循環(huán),通過(guò)上文的代碼就可以看出來(lái)务甥。
問(wèn)題2:Looper 對(duì)象是如何綁定 MessageQueue 的牡辽?或者說(shuō) Looper 對(duì)象創(chuàng)建 MessageQueue 過(guò)程。
很簡(jiǎn)單敞临,Looper 有個(gè)一成員變量 mQueue态辛,它就是 Looper 對(duì)象默認(rèn)保存的 MessageQueue。上面代碼中 Looper 有一個(gè)構(gòu)造器挺尿,新建 Looper 對(duì)象時(shí)會(huì)直接創(chuàng)建 MessageQueue 并賦值給 mQueue奏黑。
問(wèn)題2解決:在 new Looper 時(shí)就創(chuàng)建了 MessageQueue 對(duì)象并賦值給 Looper 的成員變量 mQueue。
step2: Looper --> myLooper()
// 也就是使用本類(lèi)的ThreadLocal對(duì)象獲取之前創(chuàng)建保存的Looper對(duì)象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
這個(gè)方法就是通過(guò) sThreadLocal 變量獲取當(dāng)前線程的 Looper 對(duì)象编矾,比較常用的一個(gè)方法攀涵。上文主線程 Looper 對(duì)象創(chuàng)建后使用該方法獲取了 Looper 對(duì)象。
2.1.2 開(kāi)始循環(huán)處理消息
回到最開(kāi)始的 main()
函數(shù)洽沟,在創(chuàng)建了 Looper 對(duì)象以后就調(diào)用了 Looper.loop()
來(lái)循環(huán)處理消息,貼一下大致代碼:
public static void main(){
// step1: 創(chuàng)建主線程Looper對(duì)象
Looper.prepareMainLooper();
...
// step2: 開(kāi)始循環(huán)
Loop.loop();
}
Looper --> loop()
public static void loop() {
// step1: 獲取當(dāng)前線程的 Looper 對(duì)象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// step2: 獲取 Looper 保存的 MessageQueue 對(duì)象
final MessageQueue queue = me.mQueue;
...
// step3: 循環(huán)讀取消息蜗细,如果有則調(diào)用消息對(duì)象中儲(chǔ)存的 handler 進(jìn)行發(fā)送
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
// step4: 使用 Message 對(duì)象保存的 handler 對(duì)象處理消息
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
- step1 :
myLooper()
方法就是通過(guò) ThreadLocal 獲取當(dāng)前線程的 Looper 對(duì)象裆操,注意在哪個(gè)線程使用該方法就獲取的該線程的 Looper 對(duì)象。 - step2 :
me.mQueue
炉媒,這個(gè) mQueue 就是上面問(wèn)題2所說(shuō)的在 Looper 對(duì)象創(chuàng)建時(shí)新建的 MessageQueue 變量踪区。 - step3 :接下來(lái)是一個(gè) for 循環(huán)些己,首先通過(guò)
queue.next()
來(lái)提取下一條消息晨逝,具體是怎么提取的可以參考下面文章的 4.2 節(jié):
獲取到下一條消息,如果 MessageQueue 中沒(méi)有消息莺褒,就會(huì)進(jìn)行阻塞白粉。那么如果存在消息传泊,它又是怎么放入 MessageQueue 的呢?或者說(shuō)MessageQueue 里的消息從哪里來(lái)鸭巴?Handler是如何往MessageQueue中插入消息的眷细?先不說(shuō)這個(gè),把這個(gè)問(wèn)題叫作問(wèn)題3后面分析鹃祖。
- step4 :
msg.target.dispatchMessage(msg);
這個(gè)方法最終會(huì)調(diào)用 Handler 的handleMessage(msg)
方法溪椎。同時(shí)這里又產(chǎn)生個(gè)問(wèn)題:msg.target
是何時(shí)被賦值的?,也就是說(shuō)Message 是如何綁定 Handler 的校读?先稱(chēng)之為問(wèn)題4沼侣。那么接著看 Handler 的dispatchMessage
方法:
Handler --> dispatchMessage
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();
}
public void handleMessage(Message msg) {
}
可以看到該方法最后執(zhí)行了 handleMessage()
方法,這是一個(gè)空方法也就是需要我們覆寫(xiě)并實(shí)現(xiàn)的歉秫。另外 dispatchMessage()
也體現(xiàn)出一個(gè)問(wèn)題:
消息分發(fā)的優(yōu)先級(jí):
- Message 的回調(diào)方法:
message.callback.run();
優(yōu)先級(jí)最高蛾洛; - Handler 的回調(diào)方法:
mCallback.handleMessage(msg)
優(yōu)先級(jí)次于上方; - Handler 的回調(diào)方法:
handleMessage()
優(yōu)先級(jí)最低端考。
到這里 Looper 循環(huán)并通過(guò) Handler 發(fā)送消息有一個(gè)整體的流程了雅潭,接下來(lái)分析 Handler 在消息機(jī)制中的主要作用以及和 Looper、Message 的關(guān)系却特。
2.2 Handler 的創(chuàng)建和作用
上面說(shuō)到 loop()
方法在不斷從消息隊(duì)列 MessageQueue 中取出消息(queue.next()
方法)扶供,如果沒(méi)有消息則阻塞,反之交給 Message 綁定的 Handler 處理裂明〈慌ǎ回顧一下沒(méi)解決的兩個(gè)問(wèn)題:
- 問(wèn)題3:MessageQueue 里的消息從哪里來(lái)?Handler 是如何往 MessageQueue 中插入消息的闽晦?
- 問(wèn)題4:
msg.target
是何時(shí)被賦值的扳碍?,也就是說(shuō)Message 是如何綁定 Handler 的仙蛉?
既然要解決 Handler 插入消息的問(wèn)題笋敞,就要看 Handler 發(fā)送消息的過(guò)程。
2.2.1 Handler 發(fā)送消息
Handler --> sendMessage(Message msg);
final MessageQueue mQueue;
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
// 發(fā)送延時(shí)消息
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
// 指定時(shí)間發(fā)送消息
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);
}
// 處理消息荠瘪,賦值 Message 對(duì)象的 target夯巷,消息隊(duì)列插入消息
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到調(diào)用 sendMessage(Message msg)
方法最終會(huì)調(diào)用到 enqueueMessage()
方法,這個(gè)方法主要有兩個(gè)作用:賦值 Message 對(duì)象的 target哀墓、消息隊(duì)列插入消息趁餐。
- 賦值 msg 的 target:
msg.target = this
把發(fā)送消息的 Handler 賦值給 msg 對(duì)象的 target。那么問(wèn)題 4 就解決了:Handler 執(zhí)行發(fā)送消息的過(guò)程中將自己綁定給了 Message 的 target篮绰,這樣兩者之間就產(chǎn)生了聯(lián)系后雷; - 消息隊(duì)列插入消息:
queue.enqueueMessage(msg, uptimeMillis)
queue 是 MessageQueue 的一個(gè)實(shí)例,queue.enqueueMessage(msg, uptimeMillis)
是執(zhí)行 MessageQueue 的enqueueMessage
方法來(lái)插入消息吠各。這樣問(wèn)題 3 就找到答案:Handler 在發(fā)送消息的時(shí)候執(zhí)行 MessageQueue 的enqueueMessage
方法來(lái)插入消息臀突;關(guān)于 MessageQueue 是怎么執(zhí)行插入消息的過(guò)程,參考下方文章 4.3 節(jié)
- 上面 Handler 發(fā)送消息使用了 MessageQueue 的實(shí)例 queue贾漏,可以看到這個(gè) queue 是上一個(gè)方法
sendMessageAtTime
中由 Handler 的成員變量 mQueue 賦值的惧辈,那么 mQueue 是哪來(lái)的?問(wèn)題 5:Handler 如何綁定 MessageQueue磕瓷?先劇透一下 Handler 綁定的是 Looper 的 MessageQueue 對(duì)象盒齿,Looper 的 MessageQueue 對(duì)象是在 Looper 創(chuàng)建時(shí)就 new 的念逞。
要了解 Handler 的 MessageQueue 對(duì)象是怎么賦值的就要看 Handler 的構(gòu)造函數(shù)了,Handler 創(chuàng)建的時(shí)候作了一些列操作比如獲取當(dāng)前線程的 Looper边翁,綁定 MessageQueue 對(duì)象等翎承。
2.2.2 Handler 的創(chuàng)建
下面是 Handler 無(wú)參構(gòu)造器和主要的構(gòu)造器,另外幾個(gè)重載的構(gòu)造器有些是通過(guò)傳遞不同參數(shù)調(diào)用包含兩個(gè)參數(shù)的構(gòu)造器符匾。兩個(gè)參數(shù)構(gòu)造函數(shù)第一個(gè)參數(shù)為 callback 回調(diào)叨咖,第二個(gè)函數(shù)用來(lái)標(biāo)記消息是否異步。
// 無(wú)參構(gòu)造器
public Handler() {
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());
}
}
// step1:獲取當(dāng)前線程 Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
// step2:獲取 Looper 對(duì)象綁定的 MessageQueue 對(duì)象并賦值給 Handler 的 mQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
- step1:調(diào)用
myLooper()
方法啊胶,該方法是使用 sThreadLocal 對(duì)象獲取當(dāng)前線程的 Looper 對(duì)象甸各,回顧一下:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
如果獲取的 Looper 對(duì)象為 null,說(shuō)明沒(méi)有執(zhí)行 Looper.prepare()
為當(dāng)前線程保存 Looper 變量焰坪,就會(huì)拋出 RuntimeException趣倾。這里又說(shuō)明了Handler 必須在有 Looper 的線程中使用,報(bào)錯(cuò)不說(shuō)某饰,沒(méi)有 Looper 就無(wú)法綁定 MessageQueue 對(duì)象也就無(wú)法進(jìn)行更多有關(guān)消息的操作儒恋。
- step2:
mQueue = mLooper.mQueue
說(shuō)明了 Handler 的 MessageQueue 對(duì)象是由當(dāng)前線程 Looper 的 MessageQueue 對(duì)象賦值的。這里問(wèn)題 5 解決:Handler 在創(chuàng)建時(shí)綁定了當(dāng)前線程 Looper 的 MessageQueue 對(duì)象黔漂。 - 由于 Handler 和 Looper 可以看作使用的是同一個(gè) MessageQueue 對(duì)象诫尽,所以 Handler 和 Looper 可以共享消息隊(duì)列 MessageQueue。Handler 發(fā)送消息(用 mQueue 往消息對(duì)列插入消息)炬守,Looper 可以方便的循環(huán)使用 mQueue 查詢(xún)消息牧嫉,如果查詢(xún)到消息,就可以用 Message 對(duì)象綁定的 Handler 對(duì)象 target 去處理消息减途,反之則阻塞酣藻。
既然說(shuō)到了 Handler 的構(gòu)造器,就想到一個(gè)問(wèn)題:問(wèn)題 6:關(guān)于 handler观蜗,在任何地方 new handler 都是什么線程下?這個(gè)問(wèn)題要分是否傳遞 Looper 對(duì)象來(lái)看衣洁。
- 不傳遞 Looper 創(chuàng)建 Handler:
Handler handler = new Handler();
上文就是 Handler 無(wú)參創(chuàng)建的源碼墓捻,可以看到是通過(guò)Looper.myLooper()
來(lái)獲取 Looper 對(duì)象,也就是說(shuō)對(duì)于不傳遞 Looper 對(duì)象的情況下坊夫,在哪個(gè)線程創(chuàng)建 Handler 默認(rèn)獲取的就是該線程的 Looper 對(duì)象砖第,那么 Handler 的一系列操作都是在該線程進(jìn)行的。 - 傳遞 Looper 對(duì)象創(chuàng)建 Handler:
Handler handler = new Handler(looper);
那么看看傳入 Looper 的構(gòu)造函數(shù):
public Handler(Looper looper) {
this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
this(looper, callback, false);
}
// 第一個(gè)參數(shù)是 looper 對(duì)象环凿,第二個(gè) callback 對(duì)象梧兼,第三個(gè)消息處理方式(是否異步)
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
可以看出來(lái)傳遞 Looper 對(duì)象 Handler 就直接使用了。所以對(duì)于傳遞 Looper 對(duì)象創(chuàng)建 Handler 的情況下智听,傳遞的 Looper 是哪個(gè)線程的羽杰,Handler 綁定的就是該線程渡紫。
到這里 Looper 和 Handler 就有一個(gè)大概的流程了,接下來(lái)看一個(gè)簡(jiǎn)單的子線程 Handler 使用例子:
new Thread() {
@Override
public void run() {
// step1
Looper.prepare();
// step2
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 1){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"HandlerTest",Toast.LENGTH_SHORT).show();
}
});
// step5
Looper.myLooper().quit();
}
}
};
// step3
handler.sendEmptyMessage(1);
// step4
Looper.loop();
}
}.start();
- step1: 調(diào)用
Looper.prepare();
為當(dāng)前線程創(chuàng)建 Looper 對(duì)象考赛,同時(shí)也就創(chuàng)建了 MessageQueue惕澎,之后將該線程的 Looper 對(duì)象保存在 ThreadLocal 中。注意這里的一切操作都在子線程中颜骤,如果不調(diào)用Looper.prepare()
就使用 Handler 會(huì)報(bào)錯(cuò)唧喉。 - step2: 創(chuàng)建 Handler 對(duì)象,覆寫(xiě) handleMessage 處理消息忍抽,等待該 Handler 發(fā)送的消息處理時(shí)會(huì)調(diào)用該方法八孝。
- step3: 使用 handler 發(fā)送消息,這里只是示例鸠项,畢竟自己給自己發(fā)送消息沒(méi)啥必要干跛。發(fā)送的過(guò)程中會(huì)將自己賦值給
msg.target
,然后再將消息插入到 Looper 綁定的 MessageQueue 對(duì)象中锈锤。 - step4: 調(diào)用
Looper.loop();
首先獲取當(dāng)前線程的 Looper 對(duì)象驯鳖,根據(jù) Looper 對(duì)象就可以拿到 Looper 保存的 MessageQueue 對(duì)象 mQueue。有了 MessageQueue 對(duì)象就可以 for 循環(huán)獲取它保存的消息 Message 對(duì)象久免,如果消息不存在就返回 null 阻塞浅辙,反之則使用 Message 中保存的 Handler:msg.target
來(lái)處理消息,最終調(diào)用 handleMessage 也就是之前覆寫(xiě)的方法來(lái)處理消息阎姥。 - step5: 邏輯處理完畢以后记舆,應(yīng)在最后使用 quit 方法來(lái)終止消息循環(huán),否則這個(gè)子線程就會(huì)一直處于等待的狀態(tài)呼巴,而如果退出Looper以后泽腮,這個(gè)線程就會(huì)立刻終止,因此建議不需要的時(shí)候終止Looper衣赶。
三诊赊、總結(jié)和其它
3.1 Handler、Looper府瞄、MessageQueue碧磅、Message
- Handler 用來(lái)發(fā)送消息,創(chuàng)建時(shí)先獲取默認(rèn)或傳遞來(lái)的 Looper 對(duì)象遵馆,并持有 Looper 對(duì)象包含的 MessageQueue鲸郊,發(fā)送消息時(shí)使用該 MessageQueue 對(duì)象來(lái)插入消息并把自己封裝到具體的 Message 中;
- Looper 用來(lái)為某個(gè)線程作消息循環(huán)货邓。Looper 持有一個(gè) MessageQueue 對(duì)象 mQueue秆撮,這樣就可以通過(guò)循環(huán)來(lái)獲取 MessageQueue 所維護(hù)的 Message。如果獲取的 MessageQueue 沒(méi)有消息時(shí)换况,便阻塞在 loop 的
queue.next()
中的nativePollOnce()
方法里职辨,反之則喚醒主線程繼續(xù)工作盗蟆,之后便使用 Message 封裝的 handler 對(duì)象進(jìn)行處理。 - MessageQueue 是一個(gè)消息隊(duì)列拨匆,它不直接添加消息姆涩,而是通過(guò)與 Looper 關(guān)聯(lián)的 Handler 對(duì)象來(lái)添加消息。
- Message 包含了要傳遞的數(shù)據(jù)和信息惭每。
3.2 Android中為什么主線程不會(huì)因?yàn)長(zhǎng)ooper.loop()里的死循環(huán)卡死骨饿?
這是知乎上的問(wèn)題,感覺(jué)問(wèn)的挺有意思台腥。平時(shí)可能不太會(huì)太深究這些問(wèn)題宏赘,正好有大神回答那就記錄一下吧。
- 為什么不會(huì)因?yàn)樗姥h(huán)卡死黎侈?
線程可以看作是一段可執(zhí)行代碼察署,當(dāng)代碼執(zhí)行完畢線程的生命周期就該終止了。對(duì)于主線程來(lái)說(shuō)我們不希望它執(zhí)行一段時(shí)間后退出峻汉,所以簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的贴汪,死循環(huán)便能保證不會(huì)被退出。既然是死循環(huán)那么怎么去處理消息呢休吠,通過(guò)創(chuàng)建新線程的方式扳埂。 - 為這個(gè)死循環(huán)準(zhǔn)備了一個(gè)新線程
在進(jìn)入死循環(huán)之前便創(chuàng)建了新binder線程,在代碼ActivityThread.main()中:
public static void main(){
...
Looper.prepareMainLooper();
//創(chuàng)建ActivityThread對(duì)象
ActivityThread thread = new ActivityThread();
//建立Binder通道 (創(chuàng)建新線程)
thread.attach(false);
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
// step2: 開(kāi)始循環(huán)
Loop.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
thread.attach(false)瘤礁;便會(huì)創(chuàng)建一個(gè)Binder線程(具體是指ApplicationThread阳懂,Binder的服務(wù)端,用于接收系統(tǒng)服務(wù)AMS發(fā)送來(lái)的事件)柜思,該Binder線程通過(guò)Handler將Message發(fā)送給主線程岩调。
- 主線程的死循環(huán)一直運(yùn)行是不是特別消耗CPU資源呢?
其實(shí)不然赡盘,這里就涉及到Linux pipe/epoll機(jī)制号枕,簡(jiǎn)單說(shuō)就是在主線程的MessageQueue沒(méi)有消息時(shí),便阻塞在loop的queue.next()中的nativePollOnce()方法里陨享,詳情見(jiàn)Android消息機(jī)制1-Handler(Java層)葱淳,此時(shí)主線程會(huì)釋放CPU資源進(jìn)入休眠狀態(tài),直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生霉咨,通過(guò)往pipe管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線程工作蛙紫。這里采用的epoll機(jī)制拍屑,是一種IO多路復(fù)用機(jī)制途戒,可以同時(shí)監(jiān)控多個(gè)描述符,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w)僵驰,則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮髋缯举|(zhì)同步I/O唁毒,即讀寫(xiě)是阻塞的。所以說(shuō)星爪,主線程大多數(shù)時(shí)候都是處于休眠狀態(tài)浆西,并不會(huì)消耗大量CPU資。
- Activity的生命周期是怎么實(shí)現(xiàn)在死循環(huán)體外能夠執(zhí)行起來(lái)的顽腾?
上文 main 函數(shù)有一部分獲取 sMainThreadHandler 的代碼:
final H mH = new H();
public static void main(){
...
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();
}
...
}
final Handler getHandler() {
return mH;
}
類(lèi) H 繼承了 Handler近零,在主線程創(chuàng)建時(shí)就創(chuàng)建了這個(gè) Handler 用于處理 Binder 線程發(fā)送來(lái)的消息。
Activity的生命周期都是依靠主線程的Looper.loop抄肖,當(dāng)收到不同Message時(shí)則采用相應(yīng)措施:
在H.handleMessage(msg)方法中久信,根據(jù)接收到不同的msg,執(zhí)行相應(yīng)的生命周期漓摩。
比如收到msg=H.LAUNCH_ACTIVITY裙士,則調(diào)用ActivityThread.handleLaunchActivity()方法,最終會(huì)通過(guò)反射機(jī)制管毙,創(chuàng)建Activity實(shí)例腿椎,然后再執(zhí)行Activity.onCreate()等方法;
再比如收到msg=H.PAUSE_ACTIVITY夭咬,則調(diào)用ActivityThread.handlePauseActivity()方法啃炸,最終會(huì)執(zhí)行Activity.onPause()等方法。 上述過(guò)程皱埠,我只挑核心邏輯講肮帐,真正該過(guò)程遠(yuǎn)比這復(fù)雜。
3.3 Handler 使用造成內(nèi)存泄露
- 有延時(shí)消息边器,要在Activity銷(xiāo)毀的時(shí)候移除Messages
- 匿名內(nèi)部類(lèi)導(dǎo)致的泄露改為匿名靜態(tài)內(nèi)部類(lèi)训枢,并且對(duì)上下文或者Activity使用弱引用。
具體操作可以參考文章:
參考資料:
《Android 開(kāi)發(fā)藝術(shù)探索》
Android消息機(jī)制1-Handler(Java層)
Android Handler消息機(jī)制實(shí)現(xiàn)原理