消息機制

一辈赋、概念

Android的消息機制主要是指Handler的運行機制以及Handler所附帶的MessageQueue和Looper的工作過程。Handler的主要作用是將一個任務切換到某個指定的線程中去執(zhí)行膏燕。

Android中規(guī)定訪問UI只能在主線程中進行钥屈,如果在子線程中訪問UI,那么程序就會拋出異常坝辫。為什么系統不允許在子線程中訪問UI篷就?因為如果在多線程中并發(fā)訪問可能會導致UI控件處于不可預期的狀態(tài),而如果加上鎖機制近忙,首先會讓UI訪問的邏輯變得復雜竭业,其次會降低UI訪問的效率,因此最簡單且高效的方法就是采用單線程模型來處理UI操作及舍,對于開發(fā)者來說也不是很麻煩永品,只是需要通過Handler切換一下UI訪問的執(zhí)行線程即可。

二击纬、工作流程

Handler創(chuàng)建時會采用當前線程的Looper來構建內部的消息循環(huán)系統,如果當前線程沒有Looper钾麸,那么需要為當前線程創(chuàng)建Looper更振。Handler創(chuàng)建完畢后炕桨,其內部的Looper以及MessageQueue就可以和Handler一起協同工作了,然后通過Handler的post方法將一個Runnable投遞到Handler內部的Looper中去處理肯腕,也可以通過Handler的send方法發(fā)送一個消息献宫,這個消息同樣會在Looper中去處理。其實post方法最終也是通過send方法來完成的实撒。當Handler的send方法被調用時姊途,它會調用MessageQueue的enqueueMessage方法將這個消息放入消息隊列中,然后Looper發(fā)現有新消息到來時知态,就會處理這個消息捷兰,最終消息中的Runnable或者Handler的handleMessage方法就會被調用。注意负敏,Looper是運行在創(chuàng)建Handler所在的線程中的贡茅,這樣一來Handler中的業(yè)務邏輯就被切換到創(chuàng)建Handler所在的線程中去執(zhí)行了。

三其做、工作原理

1.ThreadLocal的工作原理

ThreadLocal是一個線程內部的數據存儲類顶考,通過它可以在指定的線程中存儲數據,數據存儲以后妖泄,只有在指定線程中可以獲取到存儲的數據驹沿,對于其他線程來說則無法獲取到數據。一般來說蹈胡,當某些數據是以線程為作用域并且不同線程具有不同的數據副本的時候渊季,就可以采用ThreadLocal。比如對于Handler來說审残,它需要獲取當前線程的Looper梭域,很顯然Looper的作用域就是線程并且不同線程具有不同的Looper,這個時候通過ThreadLocal就可以輕松實現Looper在線程中的存取搅轿。
ThreadLocal使用方法如下:

ThreadLocal<Boolean> mThreadLocal = new ThreadLocal<>();
private void testThreadLocal() {
    mThreadLocal.set(true);
    Log.d(TAG, "zwm, Thread#main, mThreadLocal: " + mThreadLocal.get()); //true

    new Thread("Thread#1") {
        @Override
        public void run() {
            mThreadLocal.set(false);
            Log.d(TAG, "zwm, Thread#1, mThreadLocal: " + mThreadLocal.get()); //false
        }
    }.start();

    new Thread("Thread#2") {
        @Override
        public void run() {
            Log.d(TAG, "zwm, Thread#2, mThreadLocal: " + mThreadLocal.get()); //null
        }
    }.start();
}

2.消息隊列的工作原理

消息隊列在Android中指的是MessageQueue病涨,主要包含兩個操作,插入(enqueueMessage)和讀取(next)璧坟。enqueueMessage的作用是往消息隊列中插入一條消息既穆,而next的作用是從消息隊列中取出一條消息并將其從消息隊列中移除,next方法是一個無限循環(huán)的方法雀鹃,如果消息隊列中沒有消息幻工,那么next方法會一直阻塞。盡管MessageQueue叫消息隊列黎茎,但是它的內部實現并不是用隊列囊颅,而是通過一個單鏈表的數據結構來維護消息列表,單鏈表在插入和刪除上比較有優(yōu)勢。

3.Looper的工作原理

Looper在Android的消息機制中扮演著消息循環(huán)的角色踢代,具體來說就是它會不停地從MessageQueue中查看是否有新消息盲憎,如果有新消息就會立刻處理,否則就一直阻塞胳挎。
在子線程中創(chuàng)建Looper方法如下:

new Thread("Thread#2") {
    @Override
    public void run() {
        Looper.prepare();
        Handler handler = new Handler();
        Looper.loop();
    }
}.start();

Looper常用方法:
prepare:為當前線程創(chuàng)建一個Looper饼疙。
loop:開啟消息循環(huán)。
prepareMainLooper:給主線程也就是ActivityThread創(chuàng)建Looper慕爬。
getMainLooper:可以在任何地方獲取到主線程的Looper窑眯。
quit:直接退出Looper。
quitSafely:設定一個退出標記医窿,然后把消息隊列中的已有消息處理完畢后才安全退出磅甩。

Looper的loop方法是一個死循環(huán),它會調用MessageQueue的next方法來獲取新消息留搔,而next是一個阻塞操作更胖,當沒有消息時,next方法會一直阻塞隔显,這也會導致loop方法一直阻塞却妨。如果MessageQueue的next方法返回了新消息,Looper就會處理這條消息括眠,調用msg.target.dispatchMessage(msg)彪标,這里的msg.target就是發(fā)送這條消息的Handler對象,這樣Handler發(fā)送的消息最終又交給它的dispatchMessage方法來處理了掷豺,但是這里不同的是捞烟,Handler的dispatchMessage方法是在創(chuàng)建Handler時所使用的Looper中執(zhí)行的,這樣就成功地將代碼邏輯切換到指定的線程中去執(zhí)行了当船。

當Looper的quit方法被調用時题画,Looper就會調用MessageQueue的quit或者quitSafely方法來通知消息隊列退出,當消息隊列被標記為退出狀態(tài)時德频,它的next方法就會返回null苍息,這時Looper的loop方法就會跳出死循環(huán),即Looper退出壹置。Looper退出后竞思,通過Handler發(fā)送的消息會失敗,這個時候Handler的send方法會返回false钞护。在子線程中盖喷,如果手動為其創(chuàng)建了Looper,那么在所有的事情完成以后應該調用quit方法來終止消息循環(huán)难咕,否則這個子線程就會一直處于等待的狀態(tài)课梳,而如果退出Looper以后距辆,這個線程就會立刻終止,因此建議在不需要的時候終止Looper惦界。

4.Handler的工作原理

Handler的工作主要包含消息的發(fā)送和接收過程挑格。消息的發(fā)送可以通過post的一系列方法以及send的一系列方法來實現,post的一系列方法最終是通過send的一系列方法來實現的沾歪。Handler發(fā)送消息的過程僅僅是向消息隊列中插入了一條消息,MessageQueue的next方法就會返回這條消息給Looper雾消,Looper收到消息后就開始處理了灾搏,最終消息由Looper交由Handler處理,即Handler的dispatchMessage方法會被調用立润,這時Handler就進入了處理消息的階段狂窑。

public void dispatchMessage(Message msg) {
    if(msg.callback != null) { //Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數
        handleCallback(msg);
    } else {
        if(mCallback != null) { //Callback接口桑腮,創(chuàng)建Handler所傳遞的參數泉哈,Handler handler = new Handler(callback)
            if(mCallback.handleMessage(msg)) { //Callback的handleMessage方法
                return;
            }
        }
        handleMessage(msg); //Handler的handleMessage方法
    }
}

Handler的常用構造方法如下:

public Handler() //使用當前線程的Looper

public Handler(Looper looper) //使用特定的Looper

public Handler(Callback callback) //使用Callback接口,不需要派生Handler的子類
//在日常開發(fā)中破讨,創(chuàng)建Handler最常見的方式就是派生一個Handler的子類并重寫其handleMessage方法來處理具體的消息丛晦,
//而Callback給我們提供了另外一種使用Handler的方式,當我們不想派生子類時提陶,就可以通過Callback來實現烫沙。

四、主線程的消息循環(huán)

Android的主線程就是ActivityThread隙笆,主線程的入口方法為main锌蓄,在main方法中系統會通過Looper.prepareMainLooper()來創(chuàng)建主線程的Looper以及MessageQueue,并通過Looper.loop()來開啟主線程的消息循環(huán)撑柔。
主線程的消息循環(huán)開始了以后瘸爽,ActivityThread還需要一個Handler來和消息隊列進行交互,這個Handler就是ActivityThread.H铅忿,它內部定義了一組消息類型剪决,主要包含了四大組件的啟動和停止等過程。

ActivityThread通過ApplicationThread和AMS進行進程間通信辆沦,AMS以進程間通信的方式完成ActivityThread的請求后會回調ApplicationThread中的Binder方法昼捍,然后ApplicationThread會向H發(fā)送消息,H收到消息后會將ApplicationThread中的邏輯切換到ActivityThread中去執(zhí)行肢扯,即切換到主線程中去執(zhí)行妒茬,這個過程就是主線程的消息循環(huán)模型。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末蔚晨,一起剝皮案震驚了整個濱河市乍钻,隨后出現的幾起案子肛循,更是在濱河造成了極大的恐慌,老刑警劉巖银择,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件多糠,死亡現場離奇詭異,居然都是意外死亡浩考,警方通過查閱死者的電腦和手機夹孔,發(fā)現死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來析孽,“玉大人搭伤,你說我怎么就攤上這事⊥嗨玻” “怎么了怜俐?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長邓尤。 經常有香客問我拍鲤,道長,這世上最難降的妖魔是什么汞扎? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任季稳,我火速辦了婚禮,結果婚禮上佩捞,老公的妹妹穿的比我還像新娘绞幌。我一直安慰自己,他們只是感情好一忱,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布莲蜘。 她就那樣靜靜地躺著,像睡著了一般帘营。 火紅的嫁衣襯著肌膚如雪票渠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天芬迄,我揣著相機與錄音问顷,去河邊找鬼。 笑死禀梳,一個胖子當著我的面吹牛杜窄,可吹牛的內容都是我干的。 我是一名探鬼主播算途,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼塞耕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嘴瓤?” 一聲冷哼從身側響起扫外,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤莉钙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筛谚,有當地人在樹林里發(fā)現了一具尸體磁玉,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年驾讲,在試婚紗的時候發(fā)現自己被綠了蚊伞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡蝎毡,死狀恐怖厚柳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情沐兵,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布便监,位于F島的核電站扎谎,受9級特大地震影響,放射性物質發(fā)生泄漏烧董。R本人自食惡果不足惜毁靶,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逊移。 院中可真熱鬧预吆,春花似錦、人聲如沸胳泉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扇商。三九已至凤瘦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間案铺,已是汗流浹背蔬芥。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留控汉,地道東北人笔诵。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像姑子,于是被迫代替她去往敵國和親乎婿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容