
前言
在日常開發(fā)中,我們勢(shì)必會(huì)使用到子線程和UI線程的通信,而起著橋梁作用的就是我們常用的Handler。但是他的內(nèi)部是怎么運(yùn)作的?運(yùn)作的過程中存在什么問題?需要我們注意,本文將會(huì)詳細(xì)講解。
解析Handler

從圖中我們就可以知道了,整個(gè)Handler工作組成的包括了Handler、Looper、MessageQueue、Message這四個(gè)部分。
MessageQueue和Message分別只是一個(gè)隊(duì)列和消息實(shí)體類,自然不再多說。
而Handler和Looper的具體是怎樣的呢?
在我的模擬Handler項(xiàng)目中,已經(jīng)比較清晰的闡述了整個(gè)框架的工作流程,接下里就是結(jié)合SDK代碼的一份解析了。
整個(gè)Handler往簡(jiǎn)單了來說其實(shí)就干了兩件事情:
- 發(fā)送消息
- 處理消息
發(fā)送消息
涉及到的三個(gè)函數(shù)sendMessage()、enqueueMessage()、Looper.prepareMainLooper()。
所有事情的起源要從Looper.prepareMainLooper()開始講起。
這個(gè)函數(shù)處于ActivityThread中,沒有了解過這個(gè)類的讀者們需要知道,java編程一定是有一個(gè)主入口的,但是我們?cè)谡麄€(gè)Android編程中,從來沒有涉及過main()這個(gè)函數(shù),是因?yàn)樗呀?jīng)包含在了ActivityThread這個(gè)類中,而它已經(jīng)經(jīng)過了復(fù)雜的封裝。
接下來看下這個(gè)Looper.prepareMainLooper ()函數(shù)。
public static void prepareMainLooper() {
prepare(false); // 1
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper(); // 2
}
}
// 上述注釋1對(duì)應(yīng)的函數(shù)
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));
}
// 上述注釋2對(duì)應(yīng)的函數(shù)
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
兩小段代碼,里面用到了一個(gè)變量sThreadLocal,這個(gè)變量是使用static final修飾的,意味這全局唯一。從他主動(dòng)拋出的異常我們也可以看出Looper這個(gè)對(duì)象也是一個(gè)唯一的變量。這是我們需要掌握的第一個(gè)知識(shí)點(diǎn)。
接下來是關(guān)于sendMessage()函數(shù)
這個(gè)函數(shù)其實(shí)是一個(gè)泛稱,他并不單單指sendMessage(),他還可以是sendMessageAtTime()、sendMessageDelayed(),他們都干了一件事情——傳遞消息。通過一直向下探索,你就能知道他們最后調(diào)用的都是enqueueMessage()這個(gè)函數(shù),也就是把消息放進(jìn)了消息隊(duì)列中。

沒有很多的操作,就是我們熟悉的鏈表操作。這里沒有做展示,有興趣的朋友進(jìn)到源碼往下翻一點(diǎn),馬上就能看到了。
就這樣很簡(jiǎn)單,并且很成功的讓我們的消息進(jìn)入了消息隊(duì)列。
處理消息
接收完消息,我們要干嘛?我們?yōu)槭裁匆l(fā)消息,因?yàn)槲覀円幚戆 ?/p>
這里我們要遇到的函數(shù)有:Looper.loop()、dispatchMessage()、handleMessage()。
用過Handler的讀者們都應(yīng)該知道我們是需要重寫handleMessage()這個(gè)函數(shù)的,用于對(duì)不同的消息作出響應(yīng),所以就不再多介紹。
所以第一個(gè)講的就是Looper.loop()這個(gè)函數(shù)。


一共兩段代碼,也是至關(guān)重要的一部分。
在這個(gè)代碼中兩個(gè)至關(guān)重要的點(diǎn):
(1)首先是問題是這么一個(gè)死循環(huán)的函數(shù),怎么就沒引發(fā)ANR呢????
(2)通過dispatchMessage()如何分發(fā)這個(gè)消息的?target是什么?
先是第一個(gè)問題的解答。
網(wǎng)上的解答多種多樣。但是最關(guān)鍵的點(diǎn)其實(shí)是這樣的,ANR是圍繞loop()這個(gè)函數(shù)展開的,而ANR的出現(xiàn)也就是loop()的消息沒有得到及時(shí)的消費(fèi)。
第二個(gè)問題。
先說target這個(gè)爆紅的變量是什么。msg.target也就是說這是Message的一個(gè)元素,搜索Message就能找到如下圖示。

原來target就是一個(gè)Handler,而這個(gè)Handler就是我們對(duì)應(yīng)的主動(dòng)創(chuàng)建Handler。
然后就是dispatchMessage()函數(shù)了。
/**
* Handle system messages here.
*/
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
想來這就很清楚了,他也就是將事件分發(fā)給了handleMessage()處理,而handleMessage()又是我們自己來專門寫的。msg.callback是一個(gè)Runnable對(duì)象。
害,原來就是這樣啊。。
思考
- Handler的內(nèi)存泄漏實(shí)例。
- 為什么Handler不能在子線程創(chuàng)建?
- 為什么Handler構(gòu)造方法里面的Looper不是new出來的?
問題1:Handler的內(nèi)存泄漏實(shí)例。
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
startActivity(new Intent(MainActivity.this, HandlerTestActivity.class));
return false;
}
});
new Thread(new Runnable() {
@Override
public void run() {
// 中斷3秒
SystemClock.sleep(3000);
handler.sendEmptyMessage(0);
}
}).start();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("onDestroy", "已銷毀");
handler.removeCallbacksAndMessages(0); // 2
handler = null; // 1
}
如果不加注釋1和注釋2,這就是一段會(huì)內(nèi)存泄露的代碼,看了代碼,應(yīng)該也清楚邏輯十分簡(jiǎn)單,就是一個(gè)跳轉(zhuǎn)。推出程序后,你仍然會(huì)看到跳轉(zhuǎn),這就是Handler的內(nèi)存泄漏。
問題2: 為什么Handler不能在子線程創(chuàng)建?
這個(gè)問題其實(shí)有點(diǎn)問題,對(duì)于修改過底層的華為的操作系統(tǒng)并不存在這樣的問題,但是正常的Android原生系統(tǒng)就不行了。
代碼如下
new Thread(new Runnable() {
@Override
public void run() {
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
return false;
}
});
}
}).start();

通過對(duì)報(bào)錯(cuò)溯源,我們就能發(fā)現(xiàn)這樣一個(gè)問題。

他拿不到
Looper,因?yàn)樗皇荱I線程。其實(shí)這就是問題所在,我們上文講過
sThreadLocal這個(gè)變量,他通過一個(gè)get()函數(shù)獲取了Looper。但是這里存在一個(gè)問題,這個(gè)get(),他獲取的是什么。所以我們也就進(jìn)去看看好了。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
原來,他的取法就是從一個(gè)Map中進(jìn)行獲取的,而key,就是當(dāng)前線程,所以當(dāng)我們?cè)谧泳€程中創(chuàng)建Handler的時(shí)候,我們也是照樣拿不到Looper的,這個(gè)時(shí)候我們同樣明白了Looper也是一個(gè)唯一的,因?yàn)樗粫?huì)為我們創(chuàng)建出來的一個(gè)子線程再添加一個(gè)Looper,而是共用。
就這個(gè)問題,Google其實(shí)有給出解決方案,詳細(xì)請(qǐng)看 》》HandlerThread那些事兒
問題3:為什么Handler構(gòu)造方法里面的Looper不是new出來的?
這個(gè)問題的性質(zhì)和問題2有點(diǎn)類似了,唯一性。Looper作為一個(gè)事件處理的重要組成部分,想來我們已經(jīng)看到了,就像多道程序設(shè)計(jì)技術(shù)一樣,這是一個(gè)不受控制的過程,我們需要瘋狂的思考安全性,同步性等問題。這也是唯一性的好處,所以事件統(tǒng)一處理,處理起來也就有序。至少在我們的平時(shí)使用中已經(jīng)證明了這是一個(gè)可取的方法。
以上就是我的學(xué)習(xí)成果,如果有什么我沒有思考到的地方或是文章內(nèi)存在錯(cuò)誤,歡迎與我分享。
相關(guān)文章推薦:
手撕OkHttp
手撕AsyncTask
手撕ButterKnife
HandlerThread那些事兒