Handler,MessageQueue,Runnable與Looper(上)

前言

什么叫又熟悉又陌生纵刘,這些概念就是了。對(duì)于這些概念相信沒有人沒見過(guò)犬第,但是又沒有幾個(gè)人真正熟悉锦积,所以就一直朦朦朧朧恍恍惚惚凄凄慘慘戚戚。今天就來(lái)解決這些恩恩怨怨歉嗓,做個(gè)了斷丰介。

概念初探

Runnable,Message,MessageQueue,Looper和Handler的關(guān)系簡(jiǎn)圖.png

上面是Runnable,Message,MessageQueue,Looper和Handler的關(guān)系簡(jiǎn)圖。其中:

  • Runnable和Message可以被壓入某個(gè)MsaageQueue中鉴分,形成一個(gè)集合哮幢。注意:一般情況下某種類型的MsaageQueue只允許保存相同類型的Object。上圖中我們只是為了方便敘述才把它們混放在MessageQueue中志珍,實(shí)際源碼中需要先對(duì)Runnable進(jìn)行相應(yīng)轉(zhuǎn)換橙垢。
  • Looper循環(huán)地去做某件事
    它不斷地從MessageQueue中取出一個(gè)item,然后傳給Handler進(jìn)行處理伦糯。如此循環(huán)往復(fù)柜某,假如隊(duì)列為空,那么它會(huì)進(jìn)入休眠敛纲。
  • Handle是真正“處理事情"的地方
    它利用自身的處理機(jī)制喂击,對(duì)傳入的各種Object進(jìn)行相應(yīng)的處理并產(chǎn)生最終結(jié)果。
    用一句話來(lái)概括它們载慈,就是:
    Looper不斷獲取MessageQueue中的一個(gè)Message,然后由Handle來(lái)處理惭等。

1. Handler

不知道有沒有人注意過(guò),Handle和線程Thread是什么關(guān)系办铡。

public class Handler {...
        final MessageQueue mQueue;
        final Looper mLooper;
        final Callback mCallback;
}

從Handler的源碼中看不出與Thread有什么關(guān)系辞做,但其實(shí):

  1. 每個(gè)Thread只對(duì)應(yīng)一個(gè)Looper;
  2. 每個(gè) Looper只對(duì)應(yīng)一個(gè)MessageQueue;
  3. 每個(gè)MessageQueue中有N個(gè)Message;
  4. 每個(gè)Message中最多指定一個(gè)Handler來(lái)處理事件。
    5.一個(gè)Thread可以對(duì)應(yīng)多個(gè)Handler寡具。

Handler是應(yīng)用開發(fā)人員經(jīng)常會(huì)使用到的一個(gè)類秤茅,它有兩個(gè)方面的作用。

  • 處理Message童叠,這是它作為"處理者"的本職所在框喳。
  • 將某個(gè)Message壓入MessageQueue中课幕。
#實(shí)現(xiàn)第一個(gè)功能的相應(yīng)函數(shù)
public void dispatchMessage (Message msg); //對(duì)Message進(jìn)行分發(fā)
public void handleMessage(Message msg); //對(duì)Message進(jìn)行處理

Looper從MessageQueue中取出一個(gè)Message后,首先使用調(diào)用dispatchMessage進(jìn)行消息派發(fā)五垮;后者則根據(jù)具體的策略來(lái)將Message分發(fā)給相應(yīng)的負(fù)責(zé)人乍惊。默認(rèn)情況下Handler的派發(fā)歷程是:

Message.callback(Runnable 對(duì)象)是否為空
在不為空的情況下,將優(yōu)先通過(guò)callback來(lái)處理
Handler.mCallback是否為空
在不為空的情況下放仗,調(diào)用mCallback.handleMessage
如果前兩個(gè)對(duì)象都不存在润绎,才調(diào)用Handler.handleMessage

由此可見,Handler的擴(kuò)展子類可以通過(guò)重載dispatchMessage或者HandleMessage來(lái)改變它的默認(rèn)行為诞挨。

Handler的第二個(gè)功能莉撇,容易引起開發(fā)人員的疑惑,因?yàn)檫@樣形成了Handler->MessageQueue->Message->Handler的循環(huán)惶傻。

#實(shí)現(xiàn)第二個(gè)功能的主要方法
1. post系列
final boolean post (Runnable r);
final boolean postAtTime(Runnable r, long uptimeMills);
2. Send系列
final boolean sendEmptyMessage(int what);
final boolean sendMessageAtFrontOfQueue(Message msg);
boolean sendMessageAtTime(Message msg, long uptimeMillis);

Post和Send的作用都是將某一個(gè)消息壓入MessageQueue中棍郎,區(qū)別在于后者處理的參數(shù)直接就是Message,而Post需要先將零散的信息轉(zhuǎn)換成Message,再調(diào)用Send系列函數(shù)來(lái)執(zhí)行下一步银室。

#post方法內(nèi)部
public final boolean post(Runable r){
        return sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r){
        Meaage m = Message.obtain();
        /*
          Android系統(tǒng)會(huì)維護(hù)一個(gè)全局的Message池涂佃。當(dāng)用戶需要時(shí)可以通過(guò)obtain函數(shù)獲得,避免產(chǎn)生不必要的資源浪費(fèi)粮揉。
         */
        m.callback = r;/*將Runnable對(duì)象設(shè)置為Message的回調(diào)函數(shù)*/
        return m;
}

有上述源代碼可以看出巡李,post實(shí)質(zhì)上是根據(jù)傳入的信息抚笔,從Mseeage池中獲得一個(gè)Message并將Runbale設(shè)置為Message的回調(diào)函數(shù)扶认。最后還是使用send函數(shù)將Message添加到MessageQueue。

2. MessageQueue

MessageQueue是一個(gè)消息隊(duì)列殊橙,因而它具有隊(duì)列的所有常規(guī)操作辐宾,包括:

  • 新建隊(duì)列
    由其構(gòu)造函數(shù)和本地方法nativeInit組成。其中nativeInit會(huì)在本地創(chuàng)建一個(gè)NativeMessageQueue對(duì)象膨蛮,然后直接賦值給MessageQueue中的成員變量叠纹。這一系列操作實(shí)際都是通過(guò)內(nèi)存指針進(jìn)行的。
  • 元素入隊(duì)
    final boolean enqueueMessage(Message msg, long when)
  • 元素出隊(duì)
    final Message next()
  • 刪除元素
    final void removeMessage(Handle h, int what, Object object)
    final void removeMessage(Handler h, Runnable r, Object object)
  • 銷毀隊(duì)列
    和創(chuàng)建過(guò)程一樣敞葛,還是通過(guò)本地的NativeDestory來(lái)銷毀一個(gè)MessageQueue誉察。

3. Looper

Looper有點(diǎn)類似于發(fā)動(dòng)機(jī),它的推動(dòng)使得整個(gè)Handler機(jī)制成為活源之水惹谐,不斷的處理新的消息持偏。Looper的使用有兩種情況,即在主線程以及在工作線程氨肌。

#工作線程的使用Looper的范例鸿秆。
class LooperThread extend Thread{
        public Handler mHandler; 
        public void run() {
              Looper.prepare();
              mHandler = new Handler() {
                         public void handleMessage(Message msg){
                         //處理消息的方法
                         }        
    };
              looper.loop();//進(jìn)入主循環(huán)
  }
}

這段代碼概括起來(lái)只有三個(gè)步驟:

  1. Looper的準(zhǔn)備工作(prepare)
  2. 創(chuàng)建處理消息的Handler
  3. Looper開始運(yùn)作(loop)
    我們可以看到使用的步驟十分簡(jiǎn)單,但是仍有一些地方十分模糊怎囚,例如:looper是如何創(chuàng)建的,loop是如何與handler還有MeaasgeQueue進(jìn)行連接的卿叽。
    首先我們發(fā)現(xiàn)在上述調(diào)用中使用了Looper.prepare()函數(shù),既然要用Looper類的函數(shù),那么LooperThread中肯定就得import android.os.Looper;觀察Looper類我們發(fā)現(xiàn)在Looper類里有一個(gè)很重要的成員
static final ThreadLocal<Looper> sThreadLoal = new ThreadLocal <Looper> ();

這是一個(gè)靜態(tài)變量考婴,這就意味著在引用Looper時(shí)贩虾,sThreadLocal時(shí)就已經(jīng)存在并成功構(gòu)建。ThreadLocal對(duì)象是一種特殊的全局變量沥阱,但是這種全局只局限與線程內(nèi)整胃,這就意味著每個(gè)線程的Looper都是獨(dú)立的。Looper為我們提供了很多靜態(tài)方法喳钟,可這畢竟是公共的方法屁使,其內(nèi)部還需要有針對(duì)每個(gè)Thread的特定存儲(chǔ)空間。因而sThreadLocal肯定會(huì)創(chuàng)建一個(gè)只針對(duì)當(dāng)前線程的Looper以及其他相關(guān)的對(duì)象奔则,而且這個(gè)操作很可能在prepare()蛮寂。

private static void prepare(boolean quitAllowed){
      if(sThreadLocal.get() != null){
              throw new RuntimeException("Only one Looper may be created per thread")
      }//這個(gè)判斷保證一個(gè)線程中只有一個(gè)Looper實(shí)例。
      sThreadLocal.set(new Looper(quitAllowed));
}

由上述代碼我們可以看出易茬,sThreadLocal存儲(chǔ)了一個(gè)Looper對(duì)象酬蹋。
接下來(lái)創(chuàng)建一個(gè)Handler對(duì)象,這個(gè)步驟看起來(lái)也是平淡無(wú)奇抽莱,就是使用Handler的構(gòu)造函數(shù)創(chuàng)建一個(gè)實(shí)例范抓。那么,Handler到底與Looper是如何連接起來(lái)的呢食铐?沒錯(cuò)就是構(gòu)造函數(shù)匕垫。
Handler有很多構(gòu)造函數(shù):

public Handler();
public Handler(Callback callback);
public Handler(Looper looper);
public Handler(Looper looper, Callback callback);

之所以會(huì)有那么多參數(shù),是因?yàn)镠andler中有很多內(nèi)部變量需要初始化

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;

以第一個(gè)無(wú)參構(gòu)造函數(shù)為例:

public Handler(){
          /*省略部分代碼*/
          mLooper = Looper.myLooper();//還是通過(guò)sThreadLocal.get()來(lái)獲取當(dāng)前線程中的實(shí)例虐呻。
          ... 
          mQueue = mLooper.mQueue;//mQueue是Looper與Handler之間溝通的橋梁
     mCallback = null;
}

這樣Handler和Looper象泵,MessageQueue就聯(lián)系起來(lái)了。后續(xù)Handler執(zhí)行Post/Send兩個(gè)操作時(shí)斟叼,會(huì)將消息投遞到mQueue也就是mLooper.mQueue中偶惠,一旦Looper處理到這一消息,它又會(huì)從中調(diào)出Handler進(jìn)行處理朗涩。
好了就剩下Looper.loop()了忽孽,這部分需要借助ActivityThread講解⌒淮玻看到這里一定有一點(diǎn)累了兄一,喝口小茶,歇息幾分鐘萤悴,下一篇再仔細(xì)說(shuō)明瘾腰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市覆履,隨后出現(xiàn)的幾起案子蹋盆,更是在濱河造成了極大的恐慌费薄,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件栖雾,死亡現(xiàn)場(chǎng)離奇詭異楞抡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)析藕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門召廷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人账胧,你說(shuō)我怎么就攤上這事竞慢。” “怎么了治泥?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵筹煮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我居夹,道長(zhǎng)败潦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任准脂,我火速辦了婚禮劫扒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狸膏。我一直安慰自己沟饥,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布环戈。 她就那樣靜靜地躺著闷板,像睡著了一般。 火紅的嫁衣襯著肌膚如雪院塞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天性昭,我揣著相機(jī)與錄音拦止,去河邊找鬼。 笑死糜颠,一個(gè)胖子當(dāng)著我的面吹牛汹族,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播其兴,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼顶瞒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了元旬?” 一聲冷哼從身側(cè)響起榴徐,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤守问,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后坑资,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耗帕,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年袱贮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了仿便。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攒巍,死狀恐怖嗽仪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柒莉,我是刑警寧澤钦幔,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站常柄,受9級(jí)特大地震影響鲤氢,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜西潘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一卷玉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喷市,春花似錦相种、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至腹备,卻和暖如春衬潦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背植酥。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工镀岛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人友驮。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓漂羊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親卸留。 傳聞我的和親對(duì)象是個(gè)殘疾皇子走越,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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