Android 消息處理機(jī)制(讀后心得)

原文借鑒自:http://www.reibang.com/p/02962454adf7

搞安卓的消息處理機(jī)制應(yīng)該是最基礎(chǔ)的了,但是其中原理的話還是很少有人能深入下去,最低拜讀了大神文章堂淡,文章地址在開頭芥牌。偶有心得在此記錄下來寸爆。

從App啟動開始替废,一開始找ActivityThread類,我用的是Mac版studio绎晃,在項(xiàng)目里面打上這個(gè)單詞蜜唾,竟然沒找到,Looper,handler能找到庶艾。找了一個(gè)大神問了問袁余,雙擊shift鍵出現(xiàn)搜索,然后打上這個(gè)單詞才找到咱揍。據(jù)大神的說泌霍,默認(rèn)的只是一部分常用的framework源碼,雙擊搜索的是sdk中的android源碼述召。
代碼如下:

public final class ActivityThread { 
  public static final void main(String[] args) {
   ...... 
  Looper.prepareMainLooper();
  ...... 
  ActivityThread thread = new ActivityThread(); 
  thread.attach(false); 
  ...... 
  Looper.loop();
 ...... 
  }
 }

吐槽一下,這個(gè)main函數(shù)還是挺難找的蟹地,源碼里面是放到了類的最底部积暖,5300多行了。怪与。夺刑。

首先執(zhí)行Looper.prepareMainLooper(),這里面有個(gè)ThreadLocal 線程本地存儲區(qū)(Thread Local Storage分别,簡稱為TLS)遍愿,每個(gè)線程都有自己的私有的本地存儲區(qū)域,不同線程之間彼此不能訪問對方的TLS區(qū)域耘斩。這里線程自己的本地存儲區(qū)域存放是線程自己的Looper沼填。
可以先簡單理解它相當(dāng)于map,key是線程括授,value是Looper坞笙,那么你只要用當(dāng)前的線程就能通過sThreadLocal獲取當(dāng)前線程所屬的Looper岩饼。

Looper.prepareMainLooper()做的事件就是new了一個(gè)Looper實(shí)例并放入Looper類下面一個(gè)static的ThreadLocal<Looper> sThreadLocal靜態(tài)變量中,同時(shí)給sMainLooper賦值,給sMainLooper賦值是為了方便通過Looper.getMainLooper()快速獲取主線程的Looper薛夜,sMainLooper是主線程的Looper可能獲取會比較頻繁籍茧,避免每次都到 sThreadLocal 去查找獲取。

private Looper(boolean quitAllowed) { 
  mQueue = new MessageQueue(quitAllowed); 
  mThread = Thread.currentThread();}

在Looper的構(gòu)造函數(shù)中創(chuàng)建了消息隊(duì)列MessageQueue梯澜,并且讓Looper持有MessageQueue的引用寞冯。執(zhí)行完Looper.prepareMainLooper()
之后,主線程從普通線程轉(zhuǎn)成一個(gè)Looper線程晚伙。

ActivityThread的構(gòu)造函數(shù)并沒有做什么事只是初始化了資源管理器吮龄。

 ActivityThread() {
     mResourcesManager = ResourcesManager.getInstance();
 }

thread.attach(false);會創(chuàng)建一個(gè)Binder線程,他是在主線程進(jìn)入無限循環(huán)之前創(chuàng)建撬腾,接收系統(tǒng)服務(wù)發(fā)送的消息如LAUNCH_ACTIVITY螟蝙,PAUSE_ACTIVITY等等通過handler傳遞給主線程。

我們知道一個(gè)線程是一段可執(zhí)行的代碼民傻,當(dāng)可執(zhí)行代碼執(zhí)行完成后胰默,線程生命周期便會終止,線程就會退出漓踢,那么做為App的主線程牵署,如果代碼段執(zhí)行完了會怎樣?喧半,那么就會出現(xiàn)App啟動后執(zhí)行一段代碼后就自動退出了奴迅,這是很不合理的。所以為了防止代碼段被執(zhí)行完挺据,只能在代碼中插入一個(gè)死循環(huán)取具,那么代碼就不會被執(zhí)行完,然后自動退出扁耐,怎么在在代碼中插入一個(gè)死循環(huán)呢暇检?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會變當(dāng)前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程)婉称,Looper.loop()方法里面有一段死循環(huán)的代碼块仆,重要的就是這句:

Message msg = queue.next(); //獲取隊(duì)列中的下一條消息,可能會線程阻塞

簡單說如果在主線程的MessageQueue沒有消息時(shí)王暗,就會阻塞在loop的queue.next()方法里悔据,時(shí)候主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到有下個(gè)消息進(jìn)來時(shí)候就會喚醒主線程俗壹,在2.2 版本以前科汗,這套機(jī)制是用我們熟悉的線程的wait和notify 來實(shí)現(xiàn)的,之后的版本涉及到Linux pipe/epoll機(jī)制绷雏,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作肛捍。原理類似于I/O,讀寫是堵塞的隐绵,不占用CPU資源。

分析到這里拙毫,從應(yīng)用啟動創(chuàng)建Looper依许,創(chuàng)建消息隊(duì)列,到進(jìn)入loop方法執(zhí)行無限循環(huán)中缀蹄,那么這一塊就告一段落了峭跳,主線程已經(jīng)在死循環(huán)里輪詢等待消息了,接下來我們就要再看看缺前,系統(tǒng)是怎么發(fā)消息給主線程的蛀醉,主線程是怎么處理這些個(gè)消息的?

在準(zhǔn)備啟動一個(gè)Activity的時(shí)候衅码,系統(tǒng)服務(wù)進(jìn)程下的ActivityManagerService(簡稱AMS)線程會通過Binder發(fā)送IPC調(diào)用給APP進(jìn)程拯刁,App進(jìn)程接到到調(diào)用后,通過App進(jìn)程下的Binder線程最終調(diào)用ActivityThread類下面的scheduleLaunchActivity方法來準(zhǔn)備啟動Activity逝段,在Binder線程執(zhí)行mH.sendMessage(msg);垛玻,由主線程創(chuàng)建的Handler mH實(shí)例發(fā)送消息到主線程的消息隊(duì)列里,消息隊(duì)列從無到有奶躯,主線程堵塞被喚醒帚桩,主線程loop拿到消息,并回調(diào)mH的handleMessage 方法處理LAUNCH_ACTIVITY 等消息嘹黔。從而實(shí)現(xiàn)Activity的啟動账嚎。

1、Handler 對象在哪個(gè)線程下構(gòu)建(Handler的構(gòu)造函數(shù)在哪個(gè)線程下調(diào)用)儡蔓,那么Handler 就會持有這個(gè)線程的Looper引用和這個(gè)線程的消息隊(duì)列的引用郭蕉。因?yàn)槌钟羞@個(gè)線程的消息隊(duì)列的引用,意味著這個(gè)Handler對象可以在任意其他線程給該線程的消息隊(duì)列添加消息喂江,也意味著Handler的handlerMessage 肯定也是在該線程執(zhí)行的恳不。
2、如果該線程不是Looper線程开呐,在這個(gè)線程new Handler 就會報(bào)錯(cuò)!

Handler 有很多sendXXXX開頭的方法sendMessageAtTime规求、sendEmptyMessageDelayed筐付、sendEmptyMessage等等,都是用來給消息隊(duì)列添加消息的阻肿,那么這些方法最終都會調(diào)用enqueueMessage來實(shí)現(xiàn)消息進(jìn)入隊(duì)列瓦戚。
最后我們再看下Handler 的dispatchMessage方法,這個(gè)方法在Looper線程從消息隊(duì)列拿出來的時(shí)候,通過msg.target.dispatchMessage(msg)
調(diào)用的丛塌。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末较解,一起剝皮案震驚了整個(gè)濱河市畜疾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌印衔,老刑警劉巖啡捶,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奸焙,居然都是意外死亡瞎暑,警方通過查閱死者的電腦和手機(jī)与帆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門了赌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玄糟,你說我怎么就攤上這事阵翎∠渎伲” “怎么了辩恼?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谓形。 經(jīng)常有香客問我灶伊,道長,這世上最難降的妖魔是什么寒跳? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任聘萨,我火速辦了婚禮,結(jié)果婚禮上童太,老公的妹妹穿的比我還像新娘米辐。我一直安慰自己,他們只是感情好书释,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布翘贮。 她就那樣靜靜地躺著,像睡著了一般爆惧。 火紅的嫁衣襯著肌膚如雪狸页。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天扯再,我揣著相機(jī)與錄音芍耘,去河邊找鬼址遇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛斋竞,可吹牛的內(nèi)容都是我干的倔约。 我是一名探鬼主播,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼窃页,長吁一口氣:“原來是場噩夢啊……” “哼跺株!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脖卖,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤乒省,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后畦木,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袖扛,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年十籍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛆封。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡勾栗,死狀恐怖惨篱,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情围俘,我是刑警寧澤砸讳,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站界牡,受9級特大地震影響簿寂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜宿亡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一常遂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧挽荠,春花似錦克胳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臭脓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間腹忽,已是汗流浹背来累。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工砚作, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘹锁。 一個(gè)月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓葫录,卻偏偏與公主長得像,于是被迫代替她去往敵國和親领猾。 傳聞我的和親對象是個(gè)殘疾皇子米同,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

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