前言
(該文半年前寫于CSDN算谈,回頭看看篮迎,覺得寫的不太好惫撰,稍微修改一下)
平時開發(fā)app時,Handler簡直已經(jīng)被用爛了顶籽,它的主要工作就是負(fù)責(zé)子線程和主線程之間的通信葵腹。我相信你已經(jīng)對Handler的使用熟能生巧了高每,但是你真的了解它嗎?
深入源碼
Handler的使用
先來回想一下我們平時都是怎么使用Handler的礁蔗。
step1:初始化一個Handler對象觉义,重寫其handleMessage()方法:
Handler mHandler=new Handler() {
@Override
public void handleMessage(Message msg){
//todo
}
};
step2:發(fā)送消息通知Handler處理:
Message msg=mHandler.obtainMessage();
msg.what=0;
mHandler.sendMessage(msg);
我們經(jīng)常會在主線程中進(jìn)行step1操作,而在子線程中通過setp2來與主線程通信浴井,如執(zhí)行UI操作晒骇。那么Handler究竟是如何做到線程之間通信的呢?故事要從一個叫Looper的家伙說起磺浙。
Looper是什么
在Android中洪囤,對于每一個線程,都可以創(chuàng)建一個Looper對象(最多一個!)和多個Handler撕氧。Looper就像一個消息泵瘤缩,源源不斷的從消息池中拿到消息,交給Handler處理伦泥。
我們先來簡單的看一下Looper類剥啤,Looper類中有有四個我們必須要了解的變量:
private static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
上面定義了兩個靜態(tài)變量:一個ThreadLocal類型的變量sThreadLocal,一個Looper類型的變量sMainLooper不脯。還有兩個final變量:一個隊(duì)列類型的mQueue府怯,一個線程類型的mThread。這四個變量至關(guān)重要防楷。
一個線程想要使用Handler牺丙,就必須得創(chuàng)建一個Looper對象。那么創(chuàng)建一個Looper對象需要做什么呢复局?很簡單冲簿,一行代碼足以粟判。
Looper.prepare();
在Looper.prepare()中,Looper類會創(chuàng)建一個新的Looper對象,并放入全局的sThreadLocal中峦剔。
sThreadLocal.set(new Looper(quitAllowed));
我們再繼續(xù)深入看看new Looper(quitAllowed)档礁,很簡單,也就兩行代碼:
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
原來只是初始化mQueue和mThread這兩個變量羊异。
但是僅僅創(chuàng)建了Looper還不行事秀,還必須開啟消息循環(huán),要不然要Looper有何用野舶。開啟消息循環(huán)同樣很簡單:
Looper.Loop();
現(xiàn)在再來看一看Looper.loop()易迹,部分源碼已省略:
public static void loop() {
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;
......
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked();
}
}
這段代碼看起來一堆,其實(shí)主要工作也就在于那個for循環(huán)平道。不過還是從第一行代碼先看起吧睹欲。
第一行調(diào)用了函數(shù)myLooper(),這個函數(shù)的作用在于從sThreadLocal中取出當(dāng)前線程的Looper對象,因?yàn)閟ThreadLocal為ThreadLocal類型一屋,所以它會保證在多線程情景下窘疮,每個線程的數(shù)據(jù)互不干擾,只能取出自己的Looper對象冀墨。
接下來取出Looper對象中的mQueue變量闸衫。
final MessageQueue queue = me.mQueue;
再來看看for循環(huán),大家可以發(fā)現(xiàn)這是一個無限循環(huán)诽嘉,沒有終止條件蔚出。正是這個for循環(huán),開啟了我們的消息機(jī)制的循環(huán)虫腋,源源不斷的將消息給發(fā)送出去:
Message msg = queue.next();
msg.target.dispatchMessage(msg);
Handler與Looper的綁定
說了這么多骄酗,大家對Looper應(yīng)該稍微有點(diǎn)了解了,但是上述的代碼里似乎沒有涉及到我們使用的Handler啊悦冀,那Looper是如何與Handler進(jìn)行綁定的呢趋翻?又是怎么拿到我們的消息并進(jìn)行分發(fā)的呢?這就要看看Handler的源碼了盒蟆。
先要看一看Handler的構(gòu)造函數(shù):
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;
......
}
這里主要有兩步操作:首先是獲取當(dāng)前線程的Looper對象踏烙,賦值給本地變量,接著將Looper中的消息隊(duì)列mQueue賦值給Handler的mQueue历等。通過這兩步宙帝,Handler就與當(dāng)前線程的Looper對象綁定了。
再回到Handler的日常使用:
handler.sendEmptyMessage(int)
我們直接深入到這個方法的最底層:sendMessageAtTime(Message msg,long uptimeMills);
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);
}
先是將消息隊(duì)列mQueue賦值給局部變量queue(注意注意募闲!這個mQueue指向的可是Looper的mQueue,忘記了請看前述Hander的構(gòu)造函數(shù))愿待。再直接看最后一句浩螺, enqueueMessage(queue, msg, uptimeMillis);這里應(yīng)該是將我們發(fā)送的消息入隊(duì)了靴患。再向下挖:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
果然,這里是將msg放入了Looper的mQueue中了要出。好了鸳君,消息放到隊(duì)列中去了,有進(jìn)就有出啊患蹂,你個送信的總得把信送出去吧或颊。還記得Loop.loop()方法嗎,我們前面說該方法開起來消息機(jī)制的循環(huán)传于。loop()的for循環(huán)中最終調(diào)用了 msg.target.dispatchMessage(msg); 而msg.target, 指向的就是消息的收信人囱挑,也就是Handler,那么我們又回到了Handler的源碼:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
哈哈,看見了啥沼溜?handleMessage(msg) 啊平挑。我們平時new Hander時咋寫的記得吧。
Handler mHandler=new Handler() {
@Override
public void handleMessage(Message msg){
//todo
}
};
我們重寫了handleMessage方法系草,處理我們接收到的消息通熄,OK,消息終于送達(dá)到目的地了找都!
主線程的Looper
最后在說一點(diǎn)唇辨,我們平時開發(fā)過程中,并沒有在Activity中去初始化Looper能耻,那為什么可以使用Handler呢赏枚?其實(shí)Activity所在的主線程照樣也創(chuàng)建了Looper對象,替你干了活而已嚎京,雖然它是主線程嗡贺,也得照樣按照規(guī)則辦事啊。
Activity所在的主線程是ActivityThread鞍帝,其實(shí)這樣說并不準(zhǔn)確诫睬,因?yàn)锳ctivityThread并不是一個線程,它只是主線程的一個入口:ActivityThread中的void main(String[] args)帕涌。
public static void main(String[] args) {
......
Looper.prepareMainLooper();
......
Looper.loop();
......
}
我擦摄凡,你看它已經(jīng)默默做好了一切!
細(xì)心的你應(yīng)該發(fā)現(xiàn)了蚓曼,這里調(diào)用的是Looper.prepareMainLooper()亲澡,而不是之前所說的Looper.prepare()。是啊纫版,它可是主線程床绪,總得有點(diǎn)不一樣的地方吧,豈能完全平起平坐。
我們來挖一挖這個prepareMainLooper()癞己。
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
它首先也調(diào)用了prepare(false)方法膀斋,只不過又多做了一件事:sMainLooper = myLooper();
這時你要問了,這個變量有啥用氨匝拧仰担?大家還記得這個變量是靜態(tài)的吧,這樣在子線程中绩社,你就可以通過getMainLooper()來獲得主線程的Looper對象了摔蓝。而對于其他的子線程,因?yàn)樗鼈兊腖ooper對象只存儲在sThreadLocal中愉耙,所以只能夠取出自己的Looper對象了贮尉。
舉個例子,子線程想要在主線程中執(zhí)行一段代碼劲阎,就可以按照如下操作:
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
//todo
}
})
總結(jié)
以上就是Handler與Looper的哀怨情仇绘盟。總得來說悯仙,Looper就像是一個郵局龄毡,Handler通過sendMessage()將信件交給Looper,放到郵局的倉庫mQueue里锡垄,郵局Looper再不斷的從倉庫中取出信沦零,交還給信對應(yīng)的Handler,收信人調(diào)用handleMessage()來讀信货岭。
(轉(zhuǎn)載請注明ID:半棧工程師路操,歡迎訪問個人博客:https://halfstackdeveloper.github.io/)
歡迎關(guān)注我的知乎專欄:https://zhuanlan.zhihu.com/halfstack