Android消息機(jī)制Handler學(xué)習(xí)記錄

注:本來只是想整理成Handler部分的面試題及答案拿來背誦的巢墅,哪知一看源碼就沒停下來循衰,變成了大篇文章...本文根據(jù)源碼對Handler進(jìn)行分析坦冠,但由于能力有限旺罢,仍然和市面上的大多Handler消息機(jī)制的文章相似——即赋荆,僅探討表層邏輯狂秘,對于其中的Binder在验、native炊林、epoll之類的并不涉及彩倚。所以如果你已經(jīng)看多了表層解析筹我,就可以右上角啦~。

而且有個大問題:我把自己當(dāng)作一個旁觀者再讀了一遍這篇文章發(fā)現(xiàn)自己講的并不清楚帆离,所以各位還是自己去看一遍源碼比較好蔬蕊。至少我自己感覺在看了這一遍源碼之后,印象就很深刻了盯质,估計能記很久袁串。

Android的消息機(jī)制主要是指Handler的運行機(jī)制。

1. 什么是Handler


HandlerAndroid消息機(jī)制的上層接口呼巷。我們可以使用Handler將一個任務(wù)切換到某個指定的線程中去執(zhí)行囱修。在Android?中,Handler主要是為了解決在子線程中無法訪問UI的矛盾王悍。

2. 為什么子線程無法訪問UI


因為AndroidUI是線程不安全的破镰,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)存資源坊饶。針對這種情況泄伪,HandlerobtainMessage()方法內(nèi)部采用了享元模式殴蓬。我們應(yīng)該優(yōu)先使用HandlerobtainMessage()方法構(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)用Messageobtain()方法行施,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诉字、MessageQueueLooper以及MessageThreadLocal?這5個角色知纷。

(感覺有些復(fù)雜)

這5個角色關(guān)系有點復(fù)雜壤圃,

-----------------------------ThreadLocal-------------------------------------

先講相對簡單的ThreadLocalThreadLocal是在Looper中使用的琅轧,ThreadLocal的作用是我們可以通過它把數(shù)據(jù)存儲到指定的線程伍绳,并且只有在指定的線程才能獲取數(shù)據(jù)。

而其中把數(shù)據(jù)儲存到指定的線程這個操作在Looper.prepare()中進(jìn)行乍桂,方法調(diào)用的最后會調(diào)用ThreadLocalset方法傳入一個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()方法中傳入的是Threadnew?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辐啄,這個Mapkey其實不重要采章,因為所有線程中的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拿出來呢酥宴?這就到了我們的LooperMessageQueue環(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.?LooperMessageQueue共同協(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童鞋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亿鲜,一起剝皮案震驚了整個濱河市允蜈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒿柳,老刑警劉巖饶套,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異垒探,居然都是意外死亡妓蛮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門圾叼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛤克,“玉大人,你說我怎么就攤上這事夷蚊」辜罚” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵惕鼓,是天一觀的道長筋现。 經(jīng)常有香客問我,道長呜笑,這世上最難降的妖魔是什么夫否? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮叫胁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汞幢。我一直安慰自己驼鹅,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著输钩,像睡著了一般豺型。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上买乃,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天姻氨,我揣著相機(jī)與錄音,去河邊找鬼剪验。 笑死肴焊,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的功戚。 我是一名探鬼主播娶眷,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼啸臀!你這毒婦竟也來了届宠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤乘粒,失蹤者是張志新(化名)和其女友劉穎豌注,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灯萍,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡轧铁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了竟稳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片属桦。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖他爸,靈堂內(nèi)的尸體忽然破棺而出聂宾,到底是詐尸還是另有隱情,我是刑警寧澤诊笤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布系谐,位于F島的核電站,受9級特大地震影響讨跟,放射性物質(zhì)發(fā)生泄漏纪他。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一晾匠、第九天 我趴在偏房一處隱蔽的房頂上張望茶袒。 院中可真熱鬧,春花似錦凉馆、人聲如沸薪寓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽向叉。三九已至锥腻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間母谎,已是汗流浹背瘦黑。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留奇唤,地道東北人幸斥。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像冻记,于是被迫代替她去往敵國和親睡毒。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容