這篇“Android中Handler機(jī)制怎么應(yīng)用”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Android中Handler機(jī)制怎么應(yīng)用”文章吧。
創(chuàng)新互聯(lián)秉承實(shí)現(xiàn)全網(wǎng)價(jià)值營(yíng)銷的理念,以專業(yè)定制企業(yè)官網(wǎng),網(wǎng)站設(shè)計(jì)、網(wǎng)站制作,微信平臺(tái)小程序開發(fā),網(wǎng)頁(yè)設(shè)計(jì)制作,手機(jī)網(wǎng)站開發(fā),全網(wǎng)整合營(yíng)銷推廣幫助傳統(tǒng)企業(yè)實(shí)現(xiàn)“互聯(lián)網(wǎng)+”轉(zhuǎn)型升級(jí)專業(yè)定制企業(yè)官網(wǎng),公司注重人才、技術(shù)和管理,匯聚了一批優(yōu)秀的互聯(lián)網(wǎng)技術(shù)人才,對(duì)客戶都以感恩的心態(tài)奉獻(xiàn)自己的專業(yè)和所長(zhǎng)。
Looper
在使用Handler之前,我們必須得初始化Looper,并讓Looper跑起來。
Looper.prepare(); ... Looper.loop();
執(zhí)行上面兩條語(yǔ)句之后,Looper就可以跑起來了。先來看看對(duì)應(yīng)的源碼:
public static void prepare() { prepare(true); } 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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
必須保證一個(gè)線程中有且只有一個(gè)Looper對(duì)象,所以在初始化Looper的時(shí)候,會(huì)檢查當(dāng)前線程有沒有Looper對(duì)象。Looper的初始化會(huì)創(chuàng)建一個(gè)MessageQueue。創(chuàng)建完Looper后會(huì)放到ThreadLocal中去,關(guān)于ThreadLocal,后面會(huì)說到。
public static void loop() { // 判斷當(dāng)前線程有沒有初始化Looper 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; } ... final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { // target指的是Handler msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... msg.recycleUnchecked(); } }
方法比較長(zhǎng),所以只把最核心的代碼放了出來。省略掉的代碼中有一個(gè)比較有意思的:我們可以指定一個(gè)閾值比如說200,當(dāng)Message的處理超過200ms時(shí),就會(huì)輸出Log。這可以在開發(fā)中幫助我們發(fā)現(xiàn)一些潛在的性能問題。可惜的是,設(shè)置閾值的方法是隱藏的,無法直接調(diào)用,所以這里就不放出代碼了,感興趣的朋友自己翻一下源碼吧。
簡(jiǎn)化后的代碼可以看出邏輯十分簡(jiǎn)單,可以說Looper在當(dāng)中扮演著搬磚工的角色,從MessageQueue中取出Message,然后交給Handler去分發(fā),再去MessageQueue中取出Message...無窮無盡,就像愚公移山一樣。
看到這里,應(yīng)該多多少少會(huì)覺得有點(diǎn)不對(duì)勁,因?yàn)檫@里是一個(gè)死循環(huán),按道理來說會(huì)一直占著CPU資源的,并且消息也總有處理完的時(shí)候,難道處理完就從消息隊(duì)列返回Null,然后Looper結(jié)束嗎?顯然不是,注意看注釋might block。
MessageQueue
答案就在MessageQueue里面,直接來看一下next():
Message next() { ... int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { // Got a message. mBlocked = false; if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. for (int i = 0; i < pendingIdleHandlerCount; i++) { final IdleHandler idler = mPendingIdleHandlers[i]; mPendingIdleHandlers[i] = null; // release the reference to the handler boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; } }
代碼有點(diǎn)長(zhǎng),這次不打算省略掉一些了,因?yàn)檫@里面還有一個(gè)小彩蛋。
方法中最重要的應(yīng)該就是這一行了
nativePollOnce(ptr, nextPollTimeoutMillis);
簡(jiǎn)單來說,當(dāng)nextPollTimeoutMillis == -1時(shí),掛起當(dāng)前線程,釋放CPU資源,當(dāng)nextPollTimeoutMillis >= 0時(shí)會(huì)延時(shí)指定的時(shí)間激活一次線程,讓代碼繼續(xù)執(zhí)行下去。這里涉及到了底層的pipe管道和epoll機(jī)制,就不再講下去了(其實(shí)是因?yàn)橹v不下去了)。這也就可以回答上面的問題了,當(dāng)沒有消息的時(shí)候只需要讓線程掛起就行了,這樣可以保證不占用CPU資源的同時(shí)保住Looper的死循環(huán)。
然后我們來看消息是如何取出來的。MessageQueue中有一個(gè)Message,Message類中又有一個(gè)Message成員next,可以看出Message是一個(gè)單鏈表結(jié)構(gòu)。消息的順序是根據(jù)時(shí)間先后順序排列的。一般來說,我們要取的Message就是第一個(gè)(這里先不考慮異步消息,關(guān)于異步消息以后會(huì)講到的,又成功給自己挖了一個(gè)坑哈哈),如果當(dāng)前時(shí)間大于等于Message中指定的時(shí)間,那么將消息取出來,返回給Looper。由于此時(shí)nextPollTimeoutMillis的值為0,所以當(dāng)前面的消息處理完之后,Looper就又來取消息了。
如果當(dāng)前的時(shí)間小于Message中指定的時(shí)間,那么設(shè)置nextPollTimeoutMillis值以便下次喚醒。還有另外一種當(dāng)前已經(jīng)沒有消息了,nextPollTimeoutMillis會(huì)被設(shè)置為-1,也就是掛起線程。別急,還沒那么快呢,接著往下看。
緊接著的邏輯是判斷當(dāng)前有沒有IdleHandler,沒有的話就continue,該掛起就掛起,該延時(shí)就延時(shí),有IdleHandler的話會(huì)執(zhí)行它的queueIdle()方法。這個(gè)IdleHandler是干什么的呢?從名字應(yīng)該也能猜出個(gè)一二來,這里就不再展開講了。關(guān)于它的一些妙用可以看我之前寫的Android 啟動(dòng)優(yōu)化之延時(shí)加載。執(zhí)行完queueIdle()方法后,會(huì)將nextPollTimeoutMillis置為0,重新看一下消息隊(duì)列中有沒有新的消息。
Handler
上面將取消息的流程都講清楚了,萬(wàn)事俱備,就差往消息隊(duì)列中添加消息了,該我們最熟悉的Handler出場(chǎng)了。Handler往隊(duì)列中添加消息,主要有兩種方式:
Handler.sendXXX(); Handler.postXXX();
第一種主要是發(fā)送Message,第二種是Runnable。無論是哪種方式,最終都會(huì)進(jìn)入到MessageQueue的enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when) { ... synchronized (this) { ... msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
一般情況下,我們通過Handler發(fā)送消息的時(shí)候,會(huì)通過SystemClock.uptimeMillis()獲取一個(gè)開機(jī)時(shí)間,然后MessageQueue就會(huì)根據(jù)這個(gè)時(shí)間來對(duì)Message進(jìn)行排序。所以enqueueMessage()方法中就分了兩種情況,一種是直接可以在隊(duì)頭插入的。一種是排在中間,需要遍歷一下,然后尋一個(gè)合適的坑插入。when == 0對(duì)應(yīng)的是Handler的sendMessageAtFrontOfQueue()和postAtFrontOfQueue()方法。needWake的作用是根據(jù)情況喚醒Looper線程。
上面有一點(diǎn)還沒有講,就是Looper從MessageQueue中取出Message后,會(huì)交由Handler進(jìn)行消息的分發(fā)。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
優(yōu)先級(jí)順序是Message自帶的callback,接著是Handler自帶的callback,最后才是handleMessage()這個(gè)回調(diào)。
ThreadLocal
還記得Looper中有一個(gè)ThreadLocal吧,把它放到最后來講是因?yàn)樗梢詥为?dú)拿出來講,不想在上面干擾到整個(gè)流程。
ThreadLocal是一個(gè)數(shù)據(jù)存儲(chǔ)類,它最神奇的地方就是明明是同一個(gè)ThreadLocal對(duì)象,但是在不同線程中可以存儲(chǔ)不同的對(duì)象,比如說在線程A中存儲(chǔ)了"Hello",而在線程B中存儲(chǔ)了"World"。它們之間互相不干擾。
在Handler機(jī)制中,由于一個(gè)Looper對(duì)應(yīng)著一個(gè)線程,所以將Looper存進(jìn)ThreadLocal最合適不過了。
ThreadLocal比價(jià)常用的就set()和get()方法。分別來看看怎么實(shí)現(xiàn)的吧。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
首先是去獲取ThreadLocalMap,找得到的話直接設(shè)置值,找不到就創(chuàng)建一個(gè)。
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
看到這里,大概也能明白了。每個(gè)線程Thread中有一個(gè)ThreadLocalMap對(duì)象。通過ThreadLocal.set()方法,實(shí)際上是去獲取當(dāng)前線程中的ThreadLocalMap,線程不同,獲取到的ThreadLocalMap自然也不同。
再來看看這個(gè)ThreadLocalMap是什么來頭??搭惖淖⑨屩杏羞@么一句話:
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values.
從注釋中可以知道這就是一個(gè)定制的HashMap,并且它的Entry類指定了Key只能為ThreadLocal類型的。所以直接將它看成是一個(gè)HashMap就好了。
get()方法也好理解,就是從Map中取出值而已。大概看一下就好了。
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(); }
以上就是關(guān)于“Android中Handler機(jī)制怎么應(yīng)用”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。