理解Handler Looper MessageQueue

概述

Handler是Android常用的線程間消息工具交洗,下文對Handler梧躺,Looper劣挫,MessageQueue 涉及的代碼做一個分析册养,以此加深Handler的消息模型的認識。

Looper

Looper主要是prepare()和loop()方法压固。

prepare()為當前線程創(chuàng)建新的Looper對象球拦,存儲在ThreadLocal變量里。

Looper會創(chuàng)建Java對象MessageQueue,MessageQueue又會調(diào)用native方法nativeInit創(chuàng)建NativeMessageQueue對象帐我,mPtr存儲是NativeMessageQueue的地址坎炼,nativeInit代碼在frameworks/base/core/jni/android_os_MessageQueue.cpp?

NativeMessageQueue構(gòu)造函數(shù)中回調(diào)用Native的Looper::getForThread();獲取當前線程的native Looper對象,沒有就創(chuàng)建新的拦键,Looper代碼在/system/core/libutils/Looper.cpp中谣光。

關(guān)鍵的代碼都在Looper的構(gòu)造函數(shù)中:

1.mWakeEventFd=eventfd(0,EFD_NONBLOCK),創(chuàng)建文件描述符芬为。( Linux下的eventfd)

2.調(diào)用rebuildEpollLocked()萄金,使用epoll監(jiān)聽mWakeEventFd的可讀事件。(Epoll)

調(diào)用loop()方法媚朦,線程會進入死循環(huán)氧敢,循環(huán)中調(diào)用Looper.mQueue的next()方法,也就是MessageQueue的next()方法询张,獲取最新的msg,在調(diào)用msg.target.dispatchMessage(msg)方法來處理msg,msg的target就是發(fā)送msg的Handler對象孙乖。

MessageQueue.next()方法也是個死循環(huán),先調(diào)用native方法 nativePollOnce(ptr, nextPollTimeoutMillis)瑞侮,再調(diào)用NativeMessageQueue的pollOnce方法的圆,方法中調(diào)用native Looper對象的pollOnce方法,方法中調(diào)用Looper.pollInner方法半火,其中的關(guān)鍵方法代碼是:

epoll_wait的作用如下:

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

epfd為用epoll_create創(chuàng)建之后的句柄越妈,events是一個epoll_event*的指針,當epoll_wait這個函數(shù)操作成功之后钮糖,epoll_events里面將儲存所有的讀寫事件梅掠。max_events是當前需要監(jiān)聽的所有fd酌住。最后一個timeout是 epoll_wait的超時,為0的時候表示馬上返回阎抒,為-1的時候表示一直等下去酪我,直到有事件范圍,為任意正整數(shù)的時候表示等這么長的時間且叁,如果一直沒有事件都哭,則范圍。一般如果網(wǎng)絡(luò)主循環(huán)是單獨的線程的話逞带,可以用-1來等欺矫,這樣可以保證一些效率,如果是和主邏輯在同一個線程的話展氓,則可以用0來保證主循環(huán)的效率穆趴。

如果epoll_wait返回,就開始處理可讀事件:

如果有可讀事件的fd是nativeInit中創(chuàng)建的mWakeEventFd,就調(diào)用awoken()方法遇汞,如果是其他fd未妹,就調(diào)用pushResponse()方法,awoken()就只是從mWakeEventFd讀8個字節(jié)數(shù)據(jù)空入。

awoken()之后络它,執(zhí)行pushResponse()中獲取到的Event,調(diào)用response.request.callback->handleEvent(fd,events,data),執(zhí)行完所有其他fd的可讀事件后执庐,java代碼已經(jīng)執(zhí)行完nativePollOnce(ptr, nextPollTimeoutMillis)酪耕,方法開始往下執(zhí)行。

java對mMessages開始處理轨淌,mMessages本質(zhì)上是一個鏈表中的head對象迂烁,當前mMessages對象是否是同步屏障,只有同步屏障的消息才能 target==null(Message需要調(diào)用setAsynchronous(boolean async)才能設(shè)置消息為異步消息,默認的都是同步消息)递鹉,如果隊首是同步屏障盟步,就找后面的異步消息,如果隊首不是同步屏障躏结,直接使用當前消息却盘,然后是否到了消息的執(zhí)行時間,如果沒到媳拴,就需要繼續(xù)等待一段時間黄橘,如果到了執(zhí)行時間,就返回消息屈溉。最后要把要找到的消息從隊列里剔除塞关。

何時執(zhí)行IdleHandler

IdleHandler只有在當前找不到可以立即執(zhí)行的消息時才會執(zhí)行,而且在找到下一個消息之前只會執(zhí)行一次子巾。

Handler

1.創(chuàng)建Handler

Handler需要一個Looper來創(chuàng)建帆赢,如果不傳遞傳遞Looper會調(diào)用Looper.myLooper()小压,如果當前線程沒有Looper就會報錯,所以必須在new Handler()之前調(diào)用Looper.prepare()椰于。

2.Handler發(fā)送消息

Handler發(fā)送消息調(diào)用 sendMessageAtTime()->enqueueMessage()->MessageQueue.enqueueMessage()

根據(jù)msg.when,就是msg應(yīng)該的發(fā)生時間,把msg插入到mMessages的鏈表對用的位置中怠益,然后調(diào)用nativeWake方法。

boolean enqueueMessage(Message msg, long when)?

nativeWake()調(diào)用NativeMessageQueue.wake方法瘾婿,調(diào)用Looper->wake()方法蜻牢,向mWakeEventFd寫入8字節(jié)數(shù)據(jù),此時epoll_wait會收到可讀事件偏陪,從阻塞中返回孩饼,Java層代碼會從nativePollOnce(ptr, nextPollTimeoutMillis)返回,開始處理最早應(yīng)該發(fā)生的msg竹挡。

3.

到此代碼分析就結(jié)束了。

總結(jié)

Handler Java層面的消息模型類似于使用BlockingQueue實現(xiàn)的生產(chǎn)消費模型立膛,msg數(shù)據(jù)也只在Java層傳遞揪罕,而Handler是使用epoll實現(xiàn),Handler 在native層面也只使用了epoll的線程阻塞和喚起機制宝泵,但是epoll幾個api都是系統(tǒng)調(diào)用好啰,看上去性能還不如直接使用Java的BlockingQueue。Android的主線程為啥還使用Handler來進行線程間通信呢儿奶?這可能跟Android主線程的場景有關(guān)框往,Android 主線程除了處理Java 層消息還需要處理Input和Vsync事件的響應(yīng),這些消息都是直接發(fā)送到主線程的native的Looper中闯捎,并在response.request.callback->handleEvent(fd,events,data)的代碼中得到處理椰弊。Handler這樣的實現(xiàn)就很符合主線程的場景。簡單從Java層的線程間消息通知機制來說瓤鼻,Handler并不比BlockingQueue這樣的實現(xiàn)性能會更高秉版。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市茬祷,隨后出現(xiàn)的幾起案子清焕,更是在濱河造成了極大的恐慌,老刑警劉巖祭犯,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秸妥,死亡現(xiàn)場離奇詭異,居然都是意外死亡沃粗,警方通過查閱死者的電腦和手機粥惧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陪每,“玉大人影晓,你說我怎么就攤上這事镰吵。” “怎么了挂签?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵疤祭,是天一觀的道長。 經(jīng)常有香客問我饵婆,道長勺馆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任侨核,我火速辦了婚禮草穆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搓译。我一直安慰自己悲柱,他們只是感情好,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布些己。 她就那樣靜靜地躺著豌鸡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪段标。 梳的紋絲不亂的頭發(fā)上涯冠,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機與錄音逼庞,去河邊找鬼蛇更。 笑死,一個胖子當著我的面吹牛赛糟,可吹牛的內(nèi)容都是我干的派任。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼璧南,長吁一口氣:“原來是場噩夢啊……” “哼吨瞎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起穆咐,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤颤诀,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后对湃,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崖叫,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年拍柒,在試婚紗的時候發(fā)現(xiàn)自己被綠了心傀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡拆讯,死狀恐怖脂男,靈堂內(nèi)的尸體忽然破棺而出养叛,到底是詐尸還是另有隱情,我是刑警寧澤宰翅,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布弃甥,位于F島的核電站,受9級特大地震影響汁讼,放射性物質(zhì)發(fā)生泄漏淆攻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一嘿架、第九天 我趴在偏房一處隱蔽的房頂上張望瓶珊。 院中可真熱鬧,春花似錦耸彪、人聲如沸伞芹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽丑瞧。三九已至,卻和暖如春蜀肘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稽屏。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工扮宠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人狐榔。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓坛增,卻偏偏與公主長得像,于是被迫代替她去往敵國和親薄腻。 傳聞我的和親對象是個殘疾皇子收捣,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

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