注:本來只是想整理成Handler部分的面試題及答案拿來背誦的巢墅,哪知一看源碼就沒停下來循衰,變成了大篇文章...本文根據(jù)源碼對Handler進(jìn)行分析坦冠,但由于能力有限旺罢,仍然和市面上的大多Handler消息機(jī)制的文章相似——即赋荆,僅探討表層邏輯狂秘,對于其中的Binder在验、native炊林、epoll之類的并不涉及彩倚。所以如果你已經(jīng)看多了表層解析筹我,就可以右上角啦~。
而且有個大問題:我把自己當(dāng)作一個旁觀者再讀了一遍這篇文章發(fā)現(xiàn)自己講的并不清楚帆离,所以各位還是自己去看一遍源碼比較好蔬蕊。至少我自己感覺在看了這一遍源碼之后,印象就很深刻了盯质,估計能記很久袁串。
Android的消息機(jī)制主要是指Handler的運行機(jī)制。
1. 什么是Handler
Handler是Android消息機(jī)制的上層接口呼巷。我們可以使用Handler將一個任務(wù)切換到某個指定的線程中去執(zhí)行囱修。在Android?中,Handler主要是為了解決在子線程中無法訪問UI的矛盾王悍。
2. 為什么子線程無法訪問UI
因為Android的UI是線程不安全的破镰,UI負(fù)責(zé)與用戶的交互,如果多線程中并發(fā)訪問UI會導(dǎo)致UI處于不可控的狀態(tài)压储。其次也不能選擇使用加鎖處理鲜漩,首先因為加鎖會阻塞某些線程的執(zhí)行,降低UI訪問的效率集惋,其次加鎖會讓UI訪問的邏輯變得復(fù)雜孕似。
//該方法用于檢查當(dāng)前是否是UI線程
void checkThread() {
? if (mThread != Thread.currentThread()) {
? ? ? throw new CalledFromWrongThreadException(
? ? ? ? ? "Only the original thread that created a view hierarchy can touch its views.");
? }}
3. 為什么使用Handler
UI線程直接與用戶交互,如果在UI線程處理耗時操作刮刑,用戶將無法繼續(xù)執(zhí)行其他UI操作喉祭,用戶體驗很差养渴。其次Android規(guī)定,如果任意一個Acitivity沒有響應(yīng)5秒鐘以上就會彈出ANR窗口泛烙。因此我們可以使用Handler將耗時操作放到子線程中去執(zhí)行以避免上述情況理卑。
4. 怎么使用Handler
首先
private Handler handler = new Handler(){
? @Override? public void handleMessage(Message msg) { ?
}};
然后再子線程中處理完耗時操作后構(gòu)建一個Message對象,然后使用handler.post(Runnable r)或者handler.sendMessage(Message msg)等方法傳入Message即可蔽氨。post()方法最后也是使用sendMessage()方法藐唠,因為Runnable對象會被轉(zhuǎn)化成一個Message。
這里有一點需要注意的是鹉究,由于我們是使用Message來進(jìn)行消息傳遞宇立,所以如果每次都new?一個Message就很可能會產(chǎn)生大量用了一次就不再用的Message對象,消耗有限的內(nèi)存資源坊饶。針對這種情況泄伪,Handler的obtainMessage()方法內(nèi)部采用了享元模式殴蓬。我們應(yīng)該優(yōu)先使用Handler的obtainMessage()方法構(gòu)建Message對象匿级。
5. Handler中的享元模式
享元模式適用于可能存在大量重復(fù)對象的場景。具體的定義忘了染厅。一般經(jīng)典的享元模式會使用Map作為對象容器痘绎。不過在Message中,是通過一個單鏈表來實現(xiàn)的肖粮,具體的說就是sPool孤页,而sPool就是一個Message。
那為啥sPool這個Message對象會是一個單鏈表呢涩馆?見如下代碼:
//Message.java中
Message next; //這不就是自定義單鏈表的中next指向下一個節(jié)點嘛
調(diào)用obtainMessage()最終會調(diào)用Message的obtain()方法行施,obtain()方法先會判斷sPool如果為空,就返回一個新的Message對象魂那,不為空就返回sPool蛾号,然后sPool指向其next。
6. Handler的內(nèi)存泄露問題及其處理方法
成因:
Handler的內(nèi)存泄露問題大致是這樣形成的:就是主線程中創(chuàng)建的Handler會和主線程Looper的消息隊列MessageQueue相關(guān)聯(lián)涯雅,于是MessageQueue中的每個Message都會持有一個Handler的引用鲜结。而由于非靜態(tài)內(nèi)部類和匿名類都會隱式的持有它們所屬外部類的引用,所以就導(dǎo)致了Message持有Handler引用活逆,Handler持有其外部類的引用的引用鏈精刷。在Message被處理前,這條引用鏈會阻止垃圾回收器的回收蔗候,于是發(fā)生內(nèi)存泄露怒允。
驗證:
可以使用?handler.postDelayed()方法進(jìn)行驗證。
解決:
解決方法:1. 使用內(nèi)部靜態(tài)類構(gòu)造Handlr锈遥,因為內(nèi)部靜態(tài)類不會持有外部類的引用纫事。但是這樣的話就無法操控外部Activity的對象仰美,于是還需要增加一個隊Activity的弱引用。2. 在Activity退出的時候調(diào)用Looper的quit和quitSafely方法儿礼,以及使用對應(yīng)的handler.removeCallback方法咖杂,這些方法會最后會執(zhí)行 msg.target = null 操作,讓msg不再持有handler引用蚊夫。
7. 講解一下Handler的運行機(jī)制
private Handler myHandler = null;
new Thread("handler線程"){
@Override
public void run() {
? ? Looper.prepare();
? ? myHandler = new MyHandler();
? ? Looper.loop();? ?
? ? }
}.start();
private class MyHandler extends Handler{
? ? @Override
? ? public void handleMessage(Message msg) {
? ? ? ? super.handleMessage(msg);
? ? }
}
Handler運行機(jī)制的主要參與者是Handler诉字、MessageQueue、Looper以及Message和ThreadLocal?這5個角色知纷。
(感覺有些復(fù)雜)
這5個角色關(guān)系有點復(fù)雜壤圃,
-----------------------------ThreadLocal-------------------------------------
先講相對簡單的ThreadLocal。ThreadLocal是在Looper中使用的琅轧,ThreadLocal的作用是我們可以通過它把數(shù)據(jù)存儲到指定的線程伍绳,并且只有在指定的線程才能獲取數(shù)據(jù)。
而其中把數(shù)據(jù)儲存到指定的線程這個操作在Looper.prepare()中進(jìn)行乍桂,方法調(diào)用的最后會調(diào)用ThreadLocal的set方法傳入一個Looper對象(Looper對象包含了MessageQueue和當(dāng)前線程)冲杀。
//Looper.java中
//全局唯一一份的ThreadLocal,將會在每個ThreadLocalMap中作為key
static final ThreadLocal sThreadLocal = new ThreadLocal<~>();
private static void prepare(boolean quitAllowed) {
? ? if (sThreadLocal.get()!= null) {
? ? ? ? throw new RuntimeException("Only one Looper may be created per thread");
? ? }
? ? //這個sThreadLocal是static final 的.
? ? sThreadLocal.set(new Looper(quitAllowed));
}
private Looper(boolean quitAllowed) {
? ? mQueue = new MessageQueue(quitAllowed);
? ? mThread = Thread.currentThread();
}
然后我們可以看到ThreadLocal中的set(T value)中的createMap()方法中傳入的是Thread和new?Looper()睹酌,但并不是用Thread作為key呦权谁,如下。
//ThreadLocal.java中
public void set(T value) {
? ? Thread t = Thread.currentThread();
? ? ThreadLocalMap map = getMap(t);
? ? if (map != null)
? ? ? ? map.set(this, value);
? ? else{
? ? ? ? createMap(t, value);
? ? }
}
void createMap(Thread t, T firstValue) {
? ? t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//Thread.java中
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap(this , firstValue)中憋沿,this就是那個全局唯一份的sThrealLocal旺芽。所以我們很清楚了,在每個線程中Looper.prapare()的時候都會給當(dāng)前線程傳入一個ThreadLocalMap辐啄,這個Map的key其實不重要采章,因為所有線程中的ThreadLocal都指向同一個對象,就是Looper.java中的static final ThreadLocal sThreadLocal…因為key不變壶辜,所以如果多次調(diào)用Looper.prepare()的話悯舟,是會覆蓋的。
上面講了如何通過ThreadLocal把數(shù)據(jù)存儲到指定線程士复,至于后部分的只有在指定的線程才能獲取數(shù)據(jù)也就一目了然图谷,通過當(dāng)前線程t.threadLocals獲取線程中的變量副本,然后取出值即可阱洪。在源碼中的體現(xiàn)如下:
//Looper.java中
public static @Nullable Looper myLooper() {
? ? return sThreadLocal.get();//注意這個myLooper()便贵。返回了一個Looper對象。
}
//ThreadLocal.java中
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")
? ? ? ? ? ? //下面這個value冗荸。承璃。。之前存進(jìn)去的那個Looper對象了
? ? ? ? ? ? T result = (T)e.value;? ? ? ? ?
? ? ? ? ? ? return result;
? ? ? ? }
? ? }
? ? return setInitialValue();//這個就是在map中沒有的時候返回個null
}
ThreadLocalMap getMap(Thread t) {
? ? return t.threadLocals;
}
簡單的講蚌本,每個線程都有個存儲變量的副本盔粹,互相隔離隘梨。
那么這個特性對我們的Handler機(jī)制有什么影響呢?從上面可以了解到每個Thread其實都對應(yīng)了一個Looper舷嗡,而每個Looper都對有一個MessageQueue轴猎。
具體有什么影響需要先看一看Handler的構(gòu)造方法才能看出來。
-----------------------------Handler--------------------------------
先來看一個錯誤示例:
new Thread(){
? ? public void run(){
? ? ? ? Handler handler =new Handler();
? ? };
}.start();//正確的方法是handler前面用Looper.prepare(),后面用Looper.loop()
上述代碼會拋出“Can’t create handler inside thread that has not called Looper.prepare()”異常进萄,這是因為在Handler構(gòu)造方法中所有規(guī)定捻脖,如下:
//Hnadler中
public Handler(Callback callback, boolean async) {
? ? …省略代碼
? ? mLooper = Looper.myLooper(); //最終調(diào)用sThreadLocal.get()
? ? 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中
public static @Nullable Looper myLooper() {
? ? return sThreadLocal.get();//如果沒有獲取到Map,則返回null
}
在ThreadLocal一節(jié)的分析中可以看到sThreadLocal.get()方法在沒有獲取到ThreadLocalMap的情況下中鼠,會返回一個null可婶。
因為其下一步?mQueue?=?mLooper.mQueue;需要將Looper中的MessageQueue拿出來進(jìn)行操作,所以必然之前就需要有一個Looper對象援雇,所以就必然要先Looper.prepare()啦矛渴。
至于在主線程中我們?yōu)樯犊梢灾苯?b>new一個Handler呢?因為系統(tǒng)已經(jīng)幫我們做好了這一步惫搏,代碼如下:
//ActivityThread中
public static void main(String[] args) {
? ? …代碼省略
? ? Looper.prepareMainLooper();
? ? …代碼省略
? ? ActivityThread thread = new ActivityThread();
? ? thread.attach(false);
? ? if (sMainThreadHandler == null) {
? ? ? ? sMainThreadHandler = thread.getHandler();
? ? }
? ? …代碼省略
? ? Looper.loop();
? ? throw new RuntimeException("Main thread loop unexpectedly exited");
}
扯遠(yuǎn)了咳咳具温。
從前面的Handler的構(gòu)造方法中就可以看到,Handler其實使用的是Looper對象中的MessageQueue晶府,而Looper對象又是和Thread對應(yīng)的桂躏。
那么我們就知道了钻趋,Handler的各種發(fā)消息的操作send() , post()?等川陆,最后都會把數(shù)據(jù)Message傳遞到“那個線程(假定先命名為Thread_1)”中。
知道這個我們就可以知道為啥handler可以在不同的線程中給自己的線程的線程發(fā)消息啦蛮位。
對于使用者來說较沪,使用Handler的步驟并不復(fù)雜,定義一個Handler失仁,然后調(diào)用handler.sendMessage()發(fā)送消息尸曼,然后在handleMessage()中對消息進(jìn)行處理即可。
那么我們就從發(fā)送消息開始一步步了解Handler的消息機(jī)制萄焦。
Handler的發(fā)消息有很多種發(fā)送消息的接口控轿,如下
postAtTime(Runnable r, Object token, long uptimeMillis)
postAtTime(Runnable r, long uptimeMillis)
post(Runnable r)
postDelayed(Runnable r, long delayMillis)
sendMessageAtFrontOfQueue(Message msg)
sendMessageAtTime(Message msg, long uptimeMillis)
sendMessageDelayed(Message msg, long delayMillis)
sendEmptyMessageDelayed(int what, long delayMillis)
sendMessage(Message msg)
sendEmptyMessage(int what)
postAtFrontOfQueue(Runnable r)
……等方法
但是這么多方法,最后都是調(diào)用了enqueueMessage()方法
//Handler.java中
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
? ? msg.target = this; //這個地方要注意拂封,在這里msg持有了handler引用
? ? if (mAsynchronous) {
? ? ? ? msg.setAsynchronous(true);
? ? }
? ? return queue.enqueueMessage(msg, uptimeMillis);
}
最后的queue還有印象么茬射,就是之前說的,從Looper對象中存儲的那個MessageQueue冒签。
所以最后調(diào)用了MessageQueue.java中的enqueueMessage(Message msg, long when)方法在抛,如下:
//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
? ? if (msg.target == null) {
? ? ? ? throw new IllegalArgumentException("Message must have a target.");
? ? }
? ? …省略代碼
? ? synchronized (this) {
? ? ? ? if (mQuitting) {?
? ? ? ? ? ? …省略代碼
? ? ? ? ? ? msg.recycle();
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? …省略代碼
? ? ? ? msg.when = when;
? ? ? ? Message p = mMessages;
? ? ? ? if (p == null || when == 0 || when < p.when) {
? ? ? ? ? ? msg.next = p;
? ? ? ? ? ? mMessages = msg;
? ? ? ? } else {
? ? ? ? ? ? …省略代碼
? ? ? ? ? ? Message prev;
? ? ? ? ? ? for (;;) {
? ? ? ? ? ? ? ? prev = p;
? ? ? ? ? ? ? ? p = p.next;
? ? ? ? ? ? ? ? if (p == null || when < p.when) {
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? …省略代碼
? ? ? ? ? ? }
? ? ? ? ? ? msg.next = p; // invariant: p == prev.next
? ? ? ? ? ? prev.next = msg;
? ? ? ? }
? ? …省略代碼
? ? }
? ? return true;
}
上述的代碼不扣細(xì)節(jié)的話,就一件事萧恕,把消息插入mMessages中刚梭。
sMessages明明是Message為何能插入呢肠阱,因為在1.5的享元模式中就介紹了Message可以構(gòu)成單鏈表。所以雖然喊MessageQueue.java是消息隊列朴读,但是其實它自身并不是什么鏈表隊列結(jié)構(gòu)屹徘,它的作用是對Message這個單鏈表進(jìn)行各種操作。
所以Handler發(fā)送消息的最終結(jié)果衅金,就是把消息插入到了Message的單鏈表中缘回。而且這個Message持有Handler的引用。
那么現(xiàn)在消息插入到消息鏈表中了典挑,怎么把msg拿出來呢酥宴?這就到了我們的Looper和MessageQueue環(huán)節(jié)了。
還記得之前的Looper.prapare() – new Handler() – Looper.loop()?三部曲嗎您觉。拿消息的操作就在Looper.loop()中拙寡,當(dāng)然也在MessageQueue中,具體的流程如下琳水。
-------------------Looper和MessageQueue和Message----------------------
話不多說肆糕,上代碼:
//Looper.java中
publi 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;
? ? Binder.clearCallingIdentity();
? ? final long ident = Binder.clearCallingIdentity();
? ? …省略代碼
? ? for (;;) {
? ? ? ? Message msg = queue.next(); // might block
? ? ? ? if (msg == null) {
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? …省略代碼
? ? ? ? try {
? ? ? ? ? ? msg.target.dispatchMessage(msg);
? ? ? ? } finally {
? ? ? ? ? …省略代碼
? ? ? ? }
? ? ? ? …省略代碼
? ? ? ? final long newIdent = Binder.clearCallingIdentity();
? ? ? ? …省略代碼
? ? }
}
我留下了幾行Binder的代碼,我看不懂在孝,但是覺得這個和Binder有關(guān)的代碼肯定有很大的深意o(╯□╰ )o 诚啃,去除掉了其他我不懂的代碼之后,剩下的代碼的邏輯很清晰:先是獲取到當(dāng)前線程的MessageQueue queue = me.mQueue私沮,然后用一個死循環(huán)不斷使用queue.next()方法得到msg始赎。如果有msg,就調(diào)用handler.disptachMessage(msg)進(jìn)行消息分發(fā)仔燕。要問Handler在哪兒造垛?就是msg.target哇。
這里先不看disptachMessage(msg)的過程晰搀,我先看看for(;;)這個死循環(huán)里五辽,只有一處可以返回。就是當(dāng)queue.next()返回null的時候外恕,并且所有需要處理的msg都是從queue.next()拿來的杆逗。所以看樣子,我們的Looper.loop()中最主要的工作是由queue.next()來完成的鳞疲。
讓我們深入queue.next()看看:
Message next() {
? ? …省略代碼
? ? for (;;) {
? ? ? ? if (nextPollTimeoutMillis != 0) {
? ? ? ? ? ? Binder.flushPendingCommands();
? ? ? ? }
? ? ? ? nativePollOnce(ptr, nextPollTimeoutMillis);//這里甚至有個native方法!!!肯定有深意
? ? ? ? synchronized (this) {
? ? ? ? ? ? Message prevMsg = null;
? ? ? ? ? ? Message msg = mMessages;
? ? ? ? ? ? if (msg != null && msg.target == null) {
? ? ? ? ? ? ? ? do {
? ? ? ? ? ? ? ? ? ? prevMsg = msg;
? ? ? ? ? ? ? ? ? ? msg = msg.next;
? ? ? ? ? ? ? ? } while (msg != null && !msg.isAsynchronous());
? ? ? ? ? ? }
? ? ? ? ? ? if (msg != null) {
? ? ? ? ? ? ? ? mBlocked = false;
? ? ? ? ? ? ? ? if (prevMsg != null) {
? ? ? ? ? ? ? ? ? ? prevMsg.next = msg.next;
? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? mMessages = msg.next;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? msg.next = null;
? ? ? ? ? ? ? ? return msg;
? ? ? ? ? ? }
? ? ? ? ? ? if (mQuitting) {
? ? ? ? ? ? ? ? dispose();
? ? ? ? ? ? ? ? return null;
? ? ? ? ? ? }
? ? ? }
}
這里面我省略了一堆我看不懂的代碼罪郊,剩下的代碼邏輯也不難。這里又有一個for(;;)死循環(huán)建丧,這個死循環(huán)是不斷的mMessages中拿出msg排龄,還記得我們之前見過的mMessages嗎,之前看到mMessages是插入節(jié)點,這次見到就是要從中取出節(jié)點了橄维,取出的操作就是各種.next尺铣。
這里重點關(guān)注兩個return的地方,記住只有兩個return争舞。第一個是返回msg凛忿,第二是mQuitting == true?的時候返回null。
那么返回null會怎么樣呢竞川?還記得Looper.loop()中當(dāng)msg == null的時候會發(fā)生什么嗎店溢,當(dāng)msg = null的時候,Looper.loop()退出for(;;)死循環(huán)委乌。既然退出死循環(huán)了床牧,loop()方法也就執(zhí)行完畢,也就意味著這個Looper不再獲取新消息遭贸,handler再發(fā)什么消息都沒有用了戈咳。
甚至我們可以看到當(dāng)mQuittiong == true的時候,handler試圖enqueueMessage()?發(fā)送的消息直接就被回收了:
//MessageQueue.java中
boolean enqueueMessage(Message msg, long when) {
? …省略代碼
? ? synchronized (this) {
? ? ? ? if (mQuitting) {
? ? ? ? ? ? IllegalStateException e = new IllegalStateException(
? ? ? ? ? ? ? ? ? ? msg.target + " sending message to a Handler on a dead thread");
? ? ? ? ? ? msg.recycle();
? ? ? ? ? ? return false;
? ? ? ? }
? ? …省略代碼
}
上面的msg.recycle()和享元模式有關(guān)壕吹,大概就是這個msg直接給你抹除所有信息然后插入到sPool中著蛙。當(dāng)然sPool這個單鏈表也不是給你無限插入的,最大數(shù)量是MAX_POOL_SIZE = 50耳贬√けぃ可以自己看下源碼哈。
從上面看出mQuitting的威力巨大咒劲,那么哪兒設(shè)置這個mQuitting的值的呢顷蟆,見源碼:
//MessageQueue.java中
void quit(boolean safe) {
? ? …省略代碼
? ? synchronized (this) {
? ? ? ? if (mQuitting) {
? ? ? ? ? ? return;
? ? ? ? }
? ? ? ? mQuitting = true;
? ? ? ? …省略代碼
? ? }
}
而這個quit( boolean safe )則是在Looper中被調(diào)用,如下:
//Looper.java中
public void quit() {
? ? mQueue.quit(false);
}
public void quitSafely() {
? ? mQueue.quit(true);
}
怎么樣缎患,是不是感覺很簡單慕的。這兩者的區(qū)別在于如何處理那些延遲發(fā)送的msg,看最后調(diào)用的方法名就清楚了:removeAllMessagesLocked()和removeAllFutureMessagesLocked()挤渔。
兩個方法里都用到了msg.recycleUnchecked(),也就是說這些msg對象最后會被回收進(jìn)sPool里风题。所以理解為啥官方建議使用handler.obtainMessage()方法了吧——因為很多地方的msg都會被回收進(jìn)sPool判导。而且直接獲取現(xiàn)有msg肯定也比每次新new一個message資源消耗少,效率更高沛硅。
上面講了MessageQueue.next()返回null的特殊情況眼刃,那么如果正常返回了一個msg呢?正常的話則會調(diào)用Looper.loop()?中的msg.target.dispatchMessage(msg)方法摇肌,這個方法調(diào)用的是Handler中的dispatchMessage()方法擂红,如下
//Handler.java中
public void dispatchMessage(Message msg) {
? ? if (msg.callback != null) {
? ? ? ? handleCallback(msg);
? ? } else {
? ? ? ? if (mCallback != null) {
? ? ? ? ? ? if (mCallback.handleMessage(msg)) {
? ? ? ? ? ? ? ? return;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? handleMessage(msg);
? ? }
}
(假裝看不到handleCallback(msg)^_^因為看著有點復(fù)雜啊)最后都是調(diào)用了handleMessage(msg)围小,就是我們最最最最最最最最開始的——
private Handler handler = new Handler(){
//重寫handleMessage方法
? ? @Override
? ? public void handleMessage(Message msg) {
? ? ? ? super.handleMessage(msg);
? ? }
};
——這里面的handleMessage(msg)了昵骤,然后就可以拿msg去玩耍啦树碱。
還記得這handleMessage()最初哪兒調(diào)用的嗎?是msg.target.dispatchMessage(msg)变秦,而這個msg最終的源頭又是存在于t.sThreadLocal這個ThreadLocalMap里的成榜,所以在handleMessage(msg){}方法也就是執(zhí)行在t這個線程中啦。
于是就達(dá)成了——handler這個在外行走可以在不同的線程發(fā)送消息蹦玫,最后消息都是傳送到hanler最初創(chuàng)建的Thread里進(jìn)行處理——這個功能赎婚。
1.8 總結(jié)
總結(jié)幾個要點:
1.?ThreadLocal是個好東西,它的存在讓Thread樱溉、Handler挣输、MessageQueue綁定了起來,于是讓handler可以做到不管在哪個線程發(fā)消息福贞,最終消息都會傳送到原來的Thread里歧焦。ThreadLocalMap也是功不可沒。
2.?Looper和MessageQueue共同協(xié)作來讓整個消息隊列動起來肚医,不斷的取出新消息
3.?Message更多是用來構(gòu)造成單鏈表绢馍,不管是MessageQueue中的sMessages還是自己的享元模式中的sPool。所以最好使用handler.obtainMessage()來獲取Message對象肠套。
4. 如果上述文章哪兒有問題或者哪兒看不懂請輕輕的噴舰涌,最好是留個言我可以改善改善O(∩_∩)O
于是,一圈分析結(jié)束~ ~ ~ ~ ~ ~ ~啦啦啦啦啦你稚。
…
…
…
秋豆麻袋瓷耙,那這個msg.callback的運行機(jī)制呢?還有那些看著名字奇奇怪怪的方法呢刁赖?還有為什么UI線程死循環(huán)竟然不阻塞呢搁痛?還有….你不是說那個內(nèi)存泄露有問題嗎?你的最終結(jié)論呢宇弛?
O(╯□╰)o
Msg.callback實在不想看了鸡典,內(nèi)存泄露問題等我再問問大神,那些奇奇怪怪名字的方法如果書上木有我也研究不出啥枪芒。
但是至于為什么UI線程死循環(huán)不阻塞這個問題彻况,問的好!
看我再秀(Baidu)一波舅踪!
其實我在源碼中看到了一堆Binder纽甘、native、epoll字眼抽碌,所以我一直懷疑不阻塞這個東西應(yīng)該和Binder機(jī)制甚至和Linux的底層機(jī)制管道epoll有關(guān)悍赢。
...
算了直接放鏈接點我你可以發(fā)現(xiàn)新大陸
epoll模型
當(dāng)沒有消息的時候會epoll.wait,等待句柄寫的時候再喚醒,這個時候其實是阻塞的左权。
所有的ui操作都通過handler來發(fā)消息操作皮胡。
比如屏幕刷新16ms一個消息,你的各種點擊事件涮总,所以就會有句柄寫操作胸囱,喚醒上文的wait操作,所以不會被卡死了瀑梗。
這部分留待以后如果有機(jī)會研究Linux再詳細(xì)看吧烹笔。和Binder機(jī)制一樣,如果要研究的話抛丽,就太深入了谤职。
本文參考《Android開發(fā)藝術(shù)探索》 + 《Android源碼設(shè)計模式解析與實戰(zhàn)》+ AOSP以及感謝1Offer童鞋。