Android Handler源碼

Hanlder的使用

handler有兩種使用方式屋讶,下面開(kāi)始介紹

方式一:使用Handler.sendMessage()

在該方式中聪廉,又可以分為兩種:新建Handler子類(內(nèi)部類)娇妓、匿名 Handler子類刑巧。

/**
* 方式一:新建Handler子類(內(nèi)部類)
*/

// 步驟1:自定義Handler子類(繼承Handler類),復(fù)寫handleMessage()方法
    class mHandler extends Handler {
        // 通過(guò)復(fù)寫handlerMessage() 從而確定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
         ...// 需執(zhí)行的UI操作 
        }
    }

// 步驟2:在主線程中創(chuàng)建Handler實(shí)例
      private Handler mhandler = new mHandler();

// 步驟3:創(chuàng)建所需的消息對(duì)象
      Message msg = Message.obtain(); // 實(shí)例化消息對(duì)象
      msg.what = 1; // 消息標(biāo)識(shí)
      msg.obj = "AA"; // 消息內(nèi)容存放

 // 步驟4:在工作線程中 通過(guò)Handler發(fā)送消息到消息隊(duì)列中
 // 可通過(guò)sendMessage() / post()
 // 多線程可采用AsyncTask搂蜓、繼承Thread類、實(shí)現(xiàn)Runnable
      mHandler.sendMessage(msg);
            mHandler.sendEmptyMessage(0); // 發(fā)送空消息
        mHandler.sendMessageDelayed(message,1000); //延遲1000ms后發(fā)送攜帶數(shù)據(jù)的消息

/** 
  * 方式2:匿名內(nèi)部類
  */

// 步驟1:在主線程中 通過(guò)匿名內(nèi)部類 創(chuàng)建Handler類對(duì)象
        private Handler mhandler = new  Handler(){
              // 通過(guò)復(fù)寫handlerMessage()從而確定更新UI的操作
             @Override
              public void handleMessage(Message msg) {
                        ...// 需執(zhí)行的UI操作
                 }
          };

// 步驟2:創(chuàng)建消息對(duì)象
    Message msg = Message.obtain(); // 實(shí)例化消息對(duì)象
    msg.what = 1; // 消息標(biāo)識(shí)
    msg.obj = "AA"; // 消息內(nèi)容存放

// 步驟3:在工作線程中 通過(guò)Handler發(fā)送消息到消息隊(duì)列中
// 多線程可采用AsyncTask由蘑、繼承Thread類闽寡、實(shí)現(xiàn)Runnable
   mHandler.sendMessage(msg);
     mHandler.sendEmptyMessage(0); // 發(fā)送空消息
   mHandler.sendMessageDelayed(message,1000); //延遲1000ms后發(fā)送攜帶數(shù)據(jù)的消息

方式二:使用Handler.post()

// 步驟1:在主線程中創(chuàng)建Handler實(shí)例
    private Handler mhandler = new mHandler();

// 步驟2:在工作線程中 發(fā)送消息到消息隊(duì)列中 & 指定操作UI內(nèi)容
    // 需傳入1個(gè)Runnable對(duì)象
    mHandler.post(new Runnable() {
            @Override
            public void run() {
                ... // 需執(zhí)行的UI操作 
            }

    });

Hanlder源碼分析

Handler原理解析

主要內(nèi)容

Handler的消息機(jī)制主要包含以下內(nèi)容:

  • Message:消息
  • MessageQueue:消息隊(duì)列
  • Handler:消息管理類
  • Looper:消息循環(huán)類

Handler架構(gòu)圖

Handler的架構(gòu)圖如下:


創(chuàng)建Handler對(duì)象

具體使用

 private Handler mhandler = new  Handler(){
        // 通過(guò)復(fù)寫handlerMessage()從而確定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
                ...// 需執(zhí)行的UI操作
            }
    };

構(gòu)造方法
Handler構(gòu)造方法,調(diào)用了另一個(gè)構(gòu)造方法尼酿,this(null, false)爷狈。

public Handler() {
    this(null, false);
}

幾點(diǎn)說(shuō)明:

  • Handler需要綁定線程才能使用;綁定后裳擎,Handler的消息處理會(huì)在綁定的線程中執(zhí)行涎永。
  • 綁定方式:指定Looper對(duì)象,從而綁定了Looper對(duì)象所在的線程句惯,因?yàn)長(zhǎng)ooper對(duì)象本已綁定了對(duì)應(yīng)線程土辩。
  • 指定了Handler對(duì)象的Looper對(duì)象 = 綁定到了Looper對(duì)象所在的線程支救。

···
public Handler(Callback callback, boolean async) {

.....//省略代碼
//Looper.myLooper()作用:獲取當(dāng)前線程的Looper對(duì)象抢野;若線程無(wú)Looper對(duì)象則拋出異常
//即:若線程中無(wú)創(chuàng)建Looper對(duì)象,則也無(wú)法創(chuàng)建Handler對(duì)象
//故若需在子線程中創(chuàng)建Handler對(duì)象各墨,則需先創(chuàng)建Looper對(duì)象
mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
        "Can't create handler inside thread " + Thread.currentThread()
                + " that has not called Looper.prepare()");
}
// 綁定消息隊(duì)列對(duì)象(MessageQueue)
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;

//至此指孤,保證了handler對(duì)象的MessageQueue關(guān)聯(lián)上Looper對(duì)象中MessageQueue

}
···

從上面可以看出:

  • 當(dāng)創(chuàng)建Handler對(duì)象時(shí),通過(guò)構(gòu)造方法自動(dòng)關(guān)聯(lián)當(dāng)前線程的Looper對(duì)象和對(duì)應(yīng)的消息隊(duì)列對(duì)象(MessageQueue),從而自動(dòng)綁定了實(shí)現(xiàn)創(chuàng)建Handler對(duì)象操作的線程恃轩。

那么問(wèn)題來(lái)了结洼,到目前為止,我們不曾創(chuàng)建過(guò)Handler需要關(guān)聯(lián)的Looper對(duì)象和MessageQueue對(duì)象叉跛,那當(dāng)前線程的Looper對(duì)象和對(duì)應(yīng)的消息隊(duì)列的MessageQueue是什么時(shí)候創(chuàng)建的呢松忍?

創(chuàng)建循環(huán)對(duì)象Looper和消息隊(duì)列對(duì)象MessageQueue

  • 創(chuàng)建Looper對(duì)象的方法:Looper.prepareMainLooper()和Looper.prepare()。
  • Looper.prepareMainLooper():為主線程(UI線程)創(chuàng)建一個(gè)Looper對(duì)象筷厘。
  • Looper.prepare()為當(dāng)前線程創(chuàng)建一個(gè)Looper對(duì)象

源碼分析1:Looper.prepareMainLooper()

在Android應(yīng)用進(jìn)程啟動(dòng)時(shí)鸣峭,會(huì)默認(rèn)創(chuàng)建1個(gè)主線程ActivityThread,也叫UI線程酥艳,創(chuàng)建時(shí)摊溶,會(huì)自動(dòng)調(diào)用ActivityThread的靜態(tài)的main()方法 = 應(yīng)用程序的入口,main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象充石,同時(shí)生成一個(gè)消息隊(duì)列對(duì)象MessageQueue莫换。源碼如下:

public static void main(String[] args) {
   ../// 省略代碼
   // 1
    Looper.prepareMainLooper();
      ..///省略代碼
      // 創(chuàng)建主線程
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    ..///省略代碼
    // 開(kāi)啟消息循環(huán),后面分析
    Looper.loop();
}

// 1處為主線程創(chuàng)建1個(gè)Looper對(duì)象骤铃,同時(shí)生成1個(gè)消息隊(duì)列對(duì)象MessageQueue拉岁,繼續(xù)看prepareMainLooper(),

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 設(shè)置sMainLooper惰爬,sMainLooper是主線程的Looper膛薛,可以通過(guò)getMainLooper()獲取
        sMainLooper = myLooper();
    }
}

可以看到,Looper.prepareMainLooper()內(nèi)部其實(shí)是調(diào)用了 prepare()方法的补鼻。

源碼分析2:prepare()方法

prepare()的作用是為當(dāng)前線程創(chuàng)建1個(gè)循環(huán)器對(duì)象(Looper)哄啄,同時(shí)也生成了1個(gè)消息隊(duì)列對(duì)象(MessageQueue)。
注意风范,如果要在子線程中創(chuàng)建Looper對(duì)象咨跌,則需在子線程中手動(dòng)調(diào)用該方法。

private static void prepare(boolean quitAllowed) { 

        // 判斷sThreadLocal是否為null硼婿,否則拋出異常
        // Looper.prepare()方法不能被調(diào)用兩次 = 1個(gè)線程中只能對(duì)應(yīng)1個(gè)Looper實(shí)例
        // sThreadLocal = 1個(gè)ThreadLocal對(duì)象锌半,用于存儲(chǔ)線程的變量
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    
    // 若為初次Looper.prepare(),則創(chuàng)建Looper對(duì)象 & 存放在ThreadLocal變量中
    // Looper對(duì)象是存放在Thread線程里的
    sThreadLocal.set(new Looper(quitAllowed));
}

這里需要說(shuō)明一下寇漫,是用ThreadLocal保存Looper對(duì)象刊殉。- Handler通過(guò)綁定Looper對(duì)象的方式綁定到當(dāng)前線程。

  • 一個(gè)線程對(duì)應(yīng)著一個(gè)Looper州胳。
  • 一個(gè)Looper對(duì)應(yīng)著一個(gè)MessageQueue记焊。
  • 線程默認(rèn)是沒(méi)有Looper的,線程需要通過(guò)Looper.prepare()栓撞、綁定Handler到Looper對(duì)象遍膜、Looper.loop()來(lái)建立消息循環(huán)碗硬。
  • 主線程(UI線程),也就是ActivityThread瓢颅,在被創(chuàng)建的時(shí)候就會(huì)初始化Looper恩尾,所以主線程中可以默認(rèn)使用Handler。

綜上所述:Threadlocal是一個(gè)線程內(nèi)部的存儲(chǔ)類挽懦,可以在指定線程內(nèi)存儲(chǔ)數(shù)據(jù)翰意,數(shù)據(jù)存儲(chǔ)以后,只有指定線程可以得到存儲(chǔ)數(shù)據(jù)信柿。ThreadLocal的作用是保證一個(gè)線程對(duì)應(yīng)一個(gè)Looper猎物,同時(shí)各個(gè)線程之間的Looper互不干擾。那么ThreadLocal是怎么保證的呢角塑?

實(shí)際上ThreadLocal通過(guò)為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問(wèn)的沖突問(wèn)題蔫磨,ThreadLocal為解決多線程程序的并發(fā)問(wèn)題提供了一種新的思路。這里再多提一下圃伶,ThreadLocal和Synchronized都是為了解決多線程中相同變量的訪問(wèn)沖突問(wèn)題堤如,不同的是,

  • Synchronized是通過(guò)線程等待窒朋,犧牲時(shí)間來(lái)解決訪問(wèn)沖突
  • ThreadLocal是通過(guò)每個(gè)線程單獨(dú)一份存儲(chǔ)空間搀罢,犧牲空間來(lái)解決沖突

源碼分析3:Looper的構(gòu)造方法

以上在prepare()方法中,創(chuàng)建Looper對(duì)象并存放在sThreadLocal中侥猩,下面查看Looper的構(gòu)造方法榔至。

private Looper(boolean quitAllowed) {
        
        // 創(chuàng)建1個(gè)消息隊(duì)列對(duì)象(MessageQueue)
        // 即 當(dāng)創(chuàng)建1個(gè)Looper實(shí)例時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè)與之配對(duì)的消息隊(duì)列對(duì)象(MessageQueue)
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

可以看到欺劳,在創(chuàng)建Looper對(duì)象的時(shí)候唧取,創(chuàng)建了MessageQueue對(duì)象。
即一個(gè)Looper對(duì)應(yīng)著一個(gè)MessageQueue划提。
==根據(jù)以上總結(jié)==

  • 創(chuàng)建主線程時(shí)枫弟,會(huì)自動(dòng)調(diào)用ActivityThread的1個(gè)靜態(tài)的main();而main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對(duì)象鹏往,同時(shí)也會(huì)生成其對(duì)應(yīng)的MessageQueue對(duì)象淡诗。

    1. 即 主線程的Looper對(duì)象自動(dòng)生成,不需手動(dòng)生成伊履;而子線程的Looper對(duì)象則需手動(dòng)通過(guò)Looper.prepare()創(chuàng)建韩容。
    2. 在子線程若不手動(dòng)創(chuàng)建Looper對(duì)象 則無(wú)法生成Handler對(duì)象。
  • 根據(jù)Handler的作用(在主線程更新UI)唐瀑,故Handler實(shí)例的創(chuàng)建場(chǎng)景 主要在主線程群凶。

  • 生成Looper & MessageQueue對(duì)象后,則會(huì)自動(dòng)進(jìn)入消息循環(huán):Looper.loop()介褥。

消息循環(huán)Looper.loop()

Looper.loop()主要是消息循環(huán)座掘,從消息隊(duì)列中獲取消息,分發(fā)消息到Handler中柔滔。

public static void loop() {

        // ---  1.獲取當(dāng)前Looper的消息隊(duì)列MessageQueue -----
        
        // 第一步
        // 獲取當(dāng)前Looper對(duì)象
    final Looper me = myLooper();
    // myLooper()的作用是返回sThreadLocal存儲(chǔ)的Looper實(shí)例
    // 若me為null溢陪,則拋出異常
    // 所以在執(zhí)行l(wèi)oop()方法之前,必須執(zhí)行prepare()方法睛廊,prepare()            //的作用是創(chuàng)建Looper實(shí)例
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    
    // 第二步
    // 獲取Looper實(shí)例中的消息隊(duì)列對(duì)象MessageQueue
    final MessageQueue queue = me.mQueue;

   // ......代碼省略
   
        //------ 2. 消息循環(huán)形真,無(wú)限循環(huán) --------------
        
        // 第三步
    for (;;) {
            // 從MessageQueue中取出消息
            // 第四步
        Message msg = queue.next(); // might block
        // 如果消息為空,則退出循環(huán)
        if (msg == null) {
        // No message indicates that the message queue is quitting.
            return;
        }
  // ......代碼省略
        final long dispatchEnd;
        try {
        
                // 第五步
                // 分發(fā)消息到對(duì)應(yīng)的Handler
                // 把消息派發(fā)到msg的target屬性
                //target屬性實(shí)際上是一個(gè)handler對(duì)象
            msg.target.dispatchMessage(msg);
            
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        
        // ......代碼省略

      // 第六步
            // 回收消息
        msg.recycleUnchecked();
    }
}

消息循環(huán)Looper.loop()超全,主要作用的取出消息咆霜,通過(guò)以上代碼分析,大致分為六步:

  • 第一步嘶朱,獲取Looper對(duì)象
  • 第二步蛾坯,獲取Looper實(shí)例中的消息隊(duì)列對(duì)象MessageQueue
  • 第三步,while()無(wú)限循環(huán)遍歷
  • 第四步疏遏,通過(guò)queue.next()從MessageQueue中取出一個(gè)Message對(duì)象
  • 第五步脉课,通過(guò)msg.target.dispatchMessage(msg)來(lái)處理消息
  • 第六步,通過(guò) msg.recycleUnchecked()來(lái)回收消息

其中第四财异,五倘零,六三步涉及到消息的取出和消息的處理,在后面介紹戳寸。

消息的發(fā)送呈驶、取出和處理

對(duì)于消息的發(fā)送,取出和處理疫鹊,參照下面的文章袖瞻。

消息發(fā)送

消息的取出和處理

總結(jié)

最后再總結(jié)一下,Handler工作的流程圖:


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拆吆,一起剝皮案震驚了整個(gè)濱河市虏辫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锈拨,老刑警劉巖砌庄,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異奕枢,居然都是意外死亡娄昆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門缝彬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)萌焰,“玉大人,你說(shuō)我怎么就攤上這事谷浅“歉” “怎么了奶卓?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)撼玄。 經(jīng)常有香客問(wèn)我夺姑,道長(zhǎng),這世上最難降的妖魔是什么掌猛? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任盏浙,我火速辦了婚禮,結(jié)果婚禮上荔茬,老公的妹妹穿的比我還像新娘废膘。我一直安慰自己,他們只是感情好慕蔚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布丐黄。 她就那樣靜靜地躺著,像睡著了一般孔飒。 火紅的嫁衣襯著肌膚如雪孵稽。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天十偶,我揣著相機(jī)與錄音菩鲜,去河邊找鬼。 笑死惦积,一個(gè)胖子當(dāng)著我的面吹牛接校,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播狮崩,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蛛勉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了睦柴?” 一聲冷哼從身側(cè)響起诽凌,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坦敌,沒(méi)想到半個(gè)月后侣诵,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狱窘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年杜顺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蘸炸。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡躬络,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搭儒,到底是詐尸還是另有隱情穷当,我是刑警寧澤提茁,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站馁菜,受9級(jí)特大地震影響茴扁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜火邓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一丹弱、第九天 我趴在偏房一處隱蔽的房頂上張望德撬。 院中可真熱鬧铲咨,春花似錦、人聲如沸蜓洪。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)隆檀。三九已至摇天,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間恐仑,已是汗流浹背泉坐。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留裳仆,地道東北人腕让。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像歧斟,于是被迫代替她去往敵國(guó)和親纯丸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 一静袖、什么是Handler 1.Android SDK中用來(lái)處理異步消息的核心類2.子線程可以通過(guò)Handler來(lái)通...
    zzq_nene閱讀 157評(píng)論 0 1
  • 預(yù)備知識(shí)點(diǎn): 每一個(gè)線程都可以擁有一個(gè)Handler,但前提是需要首先調(diào)用Looper.prepare()然后創(chuàng) ...
    liu_jingwei閱讀 392評(píng)論 0 1
  • 1觉鼻、前言 在Android中,Handler消息機(jī)制十分常見(jiàn)队橙,在實(shí)現(xiàn)多線程消息切換的場(chǎng)景屢屢能看到坠陈,比如子線程切換...
    綠茵場(chǎng)上的碼者閱讀 318評(píng)論 1 0
  • Android Handler源碼淺析 相關(guān)對(duì)象 Handler可以看作CEO,負(fù)責(zé)消息的處理和發(fā)送捐康,Handle...
    ChangQin閱讀 342評(píng)論 0 2
  • 前言: 對(duì)于一個(gè)Android研發(fā)而言畅姊,親身體會(huì)就是不管在平時(shí)開(kāi)發(fā)或者面試的時(shí)候,Handler消息機(jī)制毋庸置疑都...