概述
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竹挡。
到此代碼分析就結(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)性能會更高秉版。