Android異步通信:手把手帶你深入分析 Handler機(jī)制源碼

前言

  • Android開發(fā)的多線程應(yīng)用場景中袁勺,Handler機(jī)制十分常用
  • 今天麦箍,我將手把手帶你深入分析 Handler機(jī)制的源碼腌紧,希望你們會(huì)喜歡

Anroid異步通信Handler系列文章
Android異步通信:Handler機(jī)制學(xué)習(xí)攻略
Android異步通信:Handler使用教程
Android異步通信:Handler工作原理
Android異步通信:Handler源碼分析
Android異步通信:詳解Handler內(nèi)存泄露的原因


目錄

示意圖

1. Handler 機(jī)制簡介

  • 定義
    一套 Android 消息傳遞機(jī)制

  • 作用

在多線程的應(yīng)用場景中,將工作線程中需更新UI的操作信息 傳遞到 UI主線程夜涕,從而實(shí)現(xiàn) 工作線程對UI的更新處理颤专,最終實(shí)現(xiàn)異步消息的處理

示意圖

  • 為什么要用 Handler消息傳遞機(jī)制
    答:多個(gè)線程并發(fā)更新UI的同時(shí) 保證線程安全。具體描述如下
示意圖
  • 總結(jié)
    使用Handler的原因:將工作線程需操作UI的消息 傳遞 到主線程钠乏,使得主線程可根據(jù)工作線程的需求 更新UI栖秕,從而避免線程操作不安全的問題

2. 儲(chǔ)備知識

在閱讀Handler機(jī)制的源碼分析前,請務(wù)必了解Handler的一些儲(chǔ)備知識:相關(guān)概念晓避、使用方式 & 工作原理

2.1 相關(guān)概念

關(guān)于 Handler 機(jī)制中的相關(guān)概念如下:

在下面的講解中簇捍,我將直接使用英文名講解,即 Handler俏拱、Message暑塑、Message QueueLooper锅必,希望大家先熟悉相關(guān)概念

示意圖

2.2 使用方式

  • Handler使用方式 因發(fā)送消息到消息隊(duì)列的方式不同而不同事格,共分為2種:使用Handler.sendMessage()、使用Handler.post()
  • 下面的源碼分析將依據(jù)使用步驟講解

若還不了解搞隐,請務(wù)必閱讀文章:Android:這是一份Handler消息傳遞機(jī)制 的使用教程

2.3 工作原理


3. Handler機(jī)制的核心類

在源碼分析前,先來了解Handler機(jī)制中的核心類

3.1 類說明

Handler機(jī)制 中有3個(gè)重要的類:

  • 處理器 類(Handler)
  • 消息隊(duì)列 類(MessageQueue)
  • 循環(huán)器 類(Looper)

3.2 類圖

示意圖

3.3 具體介紹

示意圖

4. 源碼分析

  • 下面的源碼分析將根據(jù) Handler的使用步驟進(jìn)行
  • Handler使用方式 因發(fā)送消息到消息隊(duì)列的方式不同而不同劣纲,共分為2種:使用Handler.sendMessage()逢捺、使用Handler.post()

若還不了解,請務(wù)必閱讀文章:Android:這是一份Handler消息傳遞機(jī)制 的使用教程

方式1:使用 Handler.sendMessage()

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

  // 步驟2:創(chuàng)建消息對象
    Message msg = Message.obtain(); // 實(shí)例化消息對象
    msg.what = 1; // 消息標(biāo)識
    msg.obj = "AA"; // 消息內(nèi)容存放
  
  // 步驟3:在工作線程中 通過Handler發(fā)送消息到消息隊(duì)列中
  // 多線程可采用AsyncTask癞季、繼承Thread類劫瞳、實(shí)現(xiàn)Runnable
   mHandler.sendMessage(msg);

  // 步驟4:開啟工作線程(同時(shí)啟動(dòng)了Handler)
  // 多線程可采用AsyncTask、繼承Thread類绷柒、實(shí)現(xiàn)Runnable
  • 源碼分析
    下面志于,我將根據(jù)上述每個(gè)步驟進(jìn)行源碼分析

步驟1:在主線程中 通過匿名內(nèi)部類 創(chuàng)建Handler類對象

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

/** 
  * 源碼分析:Handler的構(gòu)造方法
  * 作用:初始化Handler對象 & 綁定線程
  * 注:
  *   a. Handler需綁定 線程才能使用;綁定后废睦,Handler的消息處理會(huì)在綁定的線程中執(zhí)行
  *   b. 綁定方式 = 先指定Looper對象伺绽,從而綁定了 Looper對象所綁定的線程(因?yàn)長ooper對象本已綁定了對應(yīng)線程)
  *   c. 即:指定了Handler對象的 Looper對象 = 綁定到了Looper對象所在的線程
  */
  public Handler() {

            this(null, false);
            // ->>分析1

    }
/** 
  * 分析1:this(null, false) = Handler(null,false)
  */
  public Handler(Callback callback, boolean async) {

            ...// 僅貼出關(guān)鍵代碼

            // 1. 指定Looper對象
                mLooper = Looper.myLooper();
                if (mLooper == null) {
                    throw new RuntimeException(
                        "Can't create handler inside thread that has not called Looper.prepare()");
                }
                // Looper.myLooper()作用:獲取當(dāng)前線程的Looper對象郊楣;若線程無Looper對象則拋出異常
                // 即 :若線程中無創(chuàng)建Looper對象憔恳,則也無法創(chuàng)建Handler對象
                // 故 若需在子線程中創(chuàng)建Handler對象,則需先創(chuàng)建Looper對象
                // 注:可通過Loop.getMainLooper()可以獲得當(dāng)前進(jìn)程的主線程的Looper對象

            // 2. 綁定消息隊(duì)列對象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 獲取該Looper對象中保存的消息隊(duì)列對象(MessageQueue)
                // 至此净蚤,保證了handler對象 關(guān)聯(lián)上 Looper對象中MessageQueue
    }
  • 從上面可看出:
    當(dāng)創(chuàng)建Handler對象時(shí)钥组,則通過 構(gòu)造方法 自動(dòng)關(guān)聯(lián)當(dāng)前線程的Looper對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue),從而 自動(dòng)綁定了 實(shí)現(xiàn)創(chuàng)建Handler對象操作的線程

  • 那么今瀑,當(dāng)前線程的Looper對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue) 是什么時(shí)候創(chuàng)建的呢程梦?

在上述使用步驟中点把,并無 創(chuàng)建Looper對象 & 對應(yīng)的消息隊(duì)列對象(MessageQueue)這1步

步驟1前的隱式操作1:創(chuàng)建循環(huán)器對象(Looper) & 消息隊(duì)列對象(MessageQueue)

  • 創(chuàng)建Looper對象主要通過方法:Looper.prepareMainLooper()、Looper.prepare();
  • 創(chuàng)建消息隊(duì)列對象(MessageQueue)方法:創(chuàng)建Looper對象時(shí)則會(huì)自動(dòng)創(chuàng)建屿附,即:創(chuàng)建循環(huán)器對象(Looper)的同時(shí)郎逃,會(huì)自動(dòng)創(chuàng)建消息隊(duì)列對象(MessageQueue)。

方法介紹如下:


  • 源碼分析
/** 
  * 源碼分析1:Looper.prepare()
  * 作用:為當(dāng)前線程(子線程) 創(chuàng)建1個(gè)循環(huán)器對象(Looper)挺份,同時(shí)也生成了1個(gè)消息隊(duì)列對象(MessageQueue)
  * 注:需在子線程中手動(dòng)調(diào)用該方法
  */
    public static final void prepare() {
    
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 1. 判斷sThreadLocal是否為null褒翰,否則拋出異常
        //即 Looper.prepare()方法不能被調(diào)用兩次 = 1個(gè)線程中只能對應(yīng)1個(gè)Looper實(shí)例
        // 注:sThreadLocal = 1個(gè)ThreadLocal對象,用于存儲(chǔ)線程的變量

        sThreadLocal.set(new Looper(true));
        // 2. 若為初次Looper.prepare()匀泊,則創(chuàng)建Looper對象 & 存放在ThreadLocal變量中
        // 注:Looper對象是存放在Thread線程里的
        // 源碼分析Looper的構(gòu)造方法->>分析a
    }

  /** 
    * 分析a:Looper的構(gòu)造方法
    **/

        private Looper(boolean quitAllowed) {

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

            mRun = true;
            mThread = Thread.currentThread();
        }

/** 
  * 源碼分析2:Looper.prepareMainLooper()
  * 作用:為 主線程(UI線程) 創(chuàng)建1個(gè)循環(huán)器對象(Looper),同時(shí)也生成了1個(gè)消息隊(duì)列對象(MessageQueue)
  * 注:該方法在主線程(UI線程)創(chuàng)建時(shí)自動(dòng)調(diào)用各聘,即 主線程的Looper對象自動(dòng)生成揣非,不需手動(dòng)生成
  */
    // 在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的1個(gè)靜態(tài)的main()方法 = 應(yīng)用程序的入口
    // main()內(nèi)則會(huì)調(diào)用Looper.prepareMainLooper()為主線程生成1個(gè)Looper對象

      /** 
        * 源碼分析:main()
        **/
        public static void main(String[] args) {
            ... // 僅貼出關(guān)鍵代碼

            Looper.prepareMainLooper(); 
            // 1. 為主線程創(chuàng)建1個(gè)Looper對象,同時(shí)生成1個(gè)消息隊(duì)列對象(MessageQueue)
            // 方法邏輯類似Looper.prepare()
            // 注:prepare():為子線程中創(chuàng)建1個(gè)Looper對象
            
            
            ActivityThread thread = new ActivityThread(); 
            // 2. 創(chuàng)建主線程

            Looper.loop(); 
            // 3. 自動(dòng)開啟 消息循環(huán) ->>下面將詳細(xì)分析

        }

總結(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對象,同時(shí)也會(huì)生成其對應(yīng)的MessageQueue對象
  1. 即 主線程的Looper對象自動(dòng)生成箱靴,不需手動(dòng)生成腺逛;而子線程的Looper對象則需手動(dòng)通過Looper.prepare()創(chuàng)建
  2. 在子線程若不手動(dòng)創(chuàng)建Looper對象 則無法生成Handler對象
  • 根據(jù)Handler的作用(在主線程更新UI),Handler實(shí)例的創(chuàng)建場景 主要在主線程

  • 生成Looper & MessageQueue對象后衡怀,則會(huì)自動(dòng)進(jìn)入消息循環(huán):Looper.loop(),即又是另外一個(gè)隱式操作安疗。

步驟1前的隱式操作2:消息循環(huán)

此處主要分析的是Looper類中的loop()方法

/** 
  * 源碼分析: Looper.loop()
  * 作用:消息循環(huán)抛杨,即從消息隊(duì)列中獲取消息、分發(fā)消息到Handler
  * 特別注意:
  *       a. 主線程的消息循環(huán)不允許退出荐类,即無限循環(huán)
  *       b. 子線程的消息循環(huán)允許退出:調(diào)用消息隊(duì)列MessageQueue的quit()
  */
  public static void loop() {
        
        ...// 僅貼出關(guān)鍵代碼

        // 1. 獲取當(dāng)前Looper的消息隊(duì)列
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例怖现;若me為null 則拋出異常
            // 即loop()執(zhí)行前必須執(zhí)行prepare(),從而創(chuàng)建1個(gè)Looper實(shí)例
            
            final MessageQueue queue = me.mQueue;
            // 獲取Looper實(shí)例中的消息隊(duì)列對象(MessageQueue)

        // 2. 消息循環(huán)(通過for循環(huán))
            for (;;) {
            
            // 2.1 從消息隊(duì)列中取出消息
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            // next():取出消息隊(duì)列里的消息
            // 若取出的消息為空玉罐,則線程阻塞
            // ->> 分析1 

            // 2.2 派發(fā)消息到對應(yīng)的Handler
            msg.target.dispatchMessage(msg);
            // 把消息Message派發(fā)給消息對象msg的target屬性
            // target屬性實(shí)際是1個(gè)handler對象
            // ->>分析2

        // 3. 釋放消息占據(jù)的資源
        msg.recycle();
        }
}

/** 
  * 分析1:queue.next()
  * 定義:屬于消息隊(duì)列類(MessageQueue)中的方法
  * 作用:出隊(duì)消息屈嗤,即從 消息隊(duì)列中 移出該消息
  */
  Message next() {

        ...// 僅貼出關(guān)鍵代碼

        // 該參數(shù)用于確定消息隊(duì)列中是否還有消息
        // 從而決定消息隊(duì)列應(yīng)處于出隊(duì)消息狀態(tài) or 等待狀態(tài)
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

        // nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1吊输,此時(shí)消息隊(duì)列處于等待狀態(tài) 
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
     
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            // 出隊(duì)消息饶号,即 從消息隊(duì)列中取出消息:按創(chuàng)建Message對象的時(shí)間順序
            if (msg != null) {
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 取出了消息
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {

                // 若 消息隊(duì)列中已無消息,則將nextPollTimeoutMillis參數(shù)設(shè)為-1
                // 下次循環(huán)時(shí)季蚂,消息隊(duì)列則處于等待狀態(tài)
                nextPollTimeoutMillis = -1;
            }

            ......
        }
           .....
       }
}// 回到分析原處

/** 
  * 分析2:dispatchMessage(msg)
  * 定義:屬于處理者類(Handler)中的方法
  * 作用:派發(fā)消息到對應(yīng)的Handler實(shí)例 & 根據(jù)傳入的msg作出對應(yīng)的操作
  */
  public void dispatchMessage(Message msg) {

    // 1. 若msg.callback屬性不為空茫船,則代表使用了post(Runnable r)發(fā)送消息
    // 則執(zhí)行handleCallback(msg)琅束,即回調(diào)Runnable對象里復(fù)寫的run()
    // 上述結(jié)論會(huì)在講解使用“post(Runnable r)”方式時(shí)講解
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 2. 若msg.callback屬性為空,則代表使用了sendMessage(Message msg)發(fā)送消息(即此處需討論的)
            // 則執(zhí)行handleMessage(msg)算谈,即回調(diào)復(fù)寫的handleMessage(msg) ->> 分析3
            handleMessage(msg);

        }
    }

  /** 
   * 分析3:handleMessage(msg)
   * 注:該方法 = 空方法涩禀,在創(chuàng)建Handler實(shí)例時(shí)復(fù)寫 = 自定義消息處理方式
   **/
   public void handleMessage(Message msg) {  
          ... // 創(chuàng)建Handler實(shí)例時(shí)復(fù)寫
   } 

總結(jié):

  • 消息循環(huán)的操作 = 消息出隊(duì) + 分發(fā)給對應(yīng)的Handler實(shí)例
  • 分發(fā)給對應(yīng)的Handler的過程:根據(jù)出隊(duì)消息的歸屬者通過dispatchMessage(msg)進(jìn)行分發(fā),最終回調(diào)復(fù)寫的handleMessage(Message msg)然眼,從而實(shí)現(xiàn) 消息處理 的操作
  • 特別注意:在進(jìn)行消息分發(fā)時(shí)(dispatchMessage(msg))艾船,會(huì)進(jìn)行1次發(fā)送方式的判斷:
    1. msg.callback屬性不為空,則代表使用了post(Runnable r)發(fā)送消息高每,則直接回調(diào)Runnable對象里復(fù)寫的run()
    2. msg.callback屬性為空丽声,則代表使用了sendMessage(Message msg)發(fā)送消息,則回調(diào)復(fù)寫的handleMessage(msg)

至此觉义,關(guān)于步驟1的源碼分析講解完畢雁社。總結(jié)如下

示意圖

步驟2:創(chuàng)建消息對象

/** 
  * 具體使用
  */
    Message msg = Message.obtain(); // 實(shí)例化消息對象
    msg.what = 1; // 消息標(biāo)識
    msg.obj = "AA"; // 消息內(nèi)容存放

/** 
  * 源碼分析:Message.obtain()
  * 作用:創(chuàng)建消息對象
  * 注:創(chuàng)建Message對象可用關(guān)鍵字new 或 Message.obtain()
  */
  public static Message obtain() {

        // Message內(nèi)部維護(hù)了1個(gè)Message池晒骇,用于Message消息對象的復(fù)用
        // 使用obtain()則是直接從池內(nèi)獲取
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
            // 建議:使用obtain()”創(chuàng)建“消息對象霉撵,避免每次都使用new重新分配內(nèi)存
        }
        // 若池內(nèi)無消息對象可復(fù)用,則還是用關(guān)鍵字new創(chuàng)建
        return new Message();

    }

步驟3:在工作線程中 發(fā)送消息到消息隊(duì)列中

多線程的實(shí)現(xiàn)方式:AsyncTask洪囤、繼承Thread類徒坡、實(shí)現(xiàn)Runnable

/** 
  * 具體使用
  */

    mHandler.sendMessage(msg);

/** 
  * 源碼分析:mHandler.sendMessage(msg)
  * 定義:屬于處理器類(Handler)的方法
  * 作用:將消息 發(fā)送 到消息隊(duì)列中(Message ->> MessageQueue)
  */
  public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
        // ->>分析1
    }

         /** 
           * 分析1:sendMessageDelayed(msg, 0)
           **/
           public final boolean sendMessageDelayed(Message msg, long delayMillis)
            {
                if (delayMillis < 0) {
                    delayMillis = 0;
                }

                return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
                // ->> 分析2
            }

         /** 
           * 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
           **/
           public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
                    // 1. 獲取對應(yīng)的消息隊(duì)列對象(MessageQueue)
                    MessageQueue queue = mQueue;

                    // 2. 調(diào)用了enqueueMessage方法 ->>分析3
                    return enqueueMessage(queue, msg, uptimeMillis);
                }

         /** 
           * 分析3:enqueueMessage(queue, msg, uptimeMillis)
           **/
            private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
                 // 1. 將msg.target賦值為this
                 // 即 :把 當(dāng)前的Handler實(shí)例對象作為msg的target屬性
                 msg.target = this;
                 // 請回憶起上面說的Looper的loop()中消息循環(huán)時(shí),會(huì)從消息隊(duì)列中取出每個(gè)消息msg瘤缩,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
                 // 實(shí)際上則是將該消息派發(fā)給對應(yīng)的Handler實(shí)例        

                // 2. 調(diào)用消息隊(duì)列的enqueueMessage()
                // 即:Handler發(fā)送的消息喇完,最終是保存到消息隊(duì)列->>分析4
                return queue.enqueueMessage(msg, uptimeMillis);
        }

        /** 
          * 分析4:queue.enqueueMessage(msg, uptimeMillis)
          * 定義:屬于消息隊(duì)列類(MessageQueue)的方法
          * 作用:入隊(duì),即 將消息 根據(jù)時(shí)間 放入到消息隊(duì)列中(Message ->> MessageQueue)
          * 采用單鏈表實(shí)現(xiàn):提高插入消息剥啤、刪除消息的效率
          */
          boolean enqueueMessage(Message msg, long when) {

                ...// 僅貼出關(guān)鍵代碼

                synchronized (this) {

                    msg.markInUse();
                    msg.when = when;
                    Message p = mMessages;
                    boolean needWake;

                    // 判斷消息隊(duì)列里有無消息
                        // a. 若無锦溪,則將當(dāng)前插入的消息 作為隊(duì)頭 & 若此時(shí)消息隊(duì)列處于等待狀態(tài),則喚醒
                        if (p == null || when == 0 || when < p.when) {
                            msg.next = p;
                            mMessages = msg;
                            needWake = mBlocked;
                        } else {
                            needWake = mBlocked && p.target == null && msg.isAsynchronous();
                            Message prev;

                        // b. 判斷消息隊(duì)列里有消息府怯,則根據(jù) 消息(Message)創(chuàng)建的時(shí)間 插入到隊(duì)列中
                            for (;;) {
                                prev = p;
                                p = p.next;
                                if (p == null || when < p.when) {
                                    break;
                                }
                                if (needWake && p.isAsynchronous()) {
                                    needWake = false;
                                }
                            }

                            msg.next = p; 
                            prev.next = msg;
                        }

                        if (needWake) {
                            nativeWake(mPtr);
                        }
                    }
                    return true;
            }

// 之后刻诊,隨著Looper對象的無限消息循環(huán)
// 不斷從消息隊(duì)列中取出Handler發(fā)送的消息 & 分發(fā)到對應(yīng)Handler
// 最終回調(diào)Handler.handleMessage()處理消息
  • 總結(jié)
    Handler發(fā)送消息的本質(zhì) = 為該消息定義target屬性(即本身實(shí)例對象) & 將消息入隊(duì)到綁定線程的消息隊(duì)列中。具體如下:
示意圖

至此牺丙,關(guān)于使用 Handler.sendMessage()的源碼解析完畢

總結(jié)

下面则涯,將順著文章:Android Handler:圖文解析 Handler通信機(jī)制 的工作原理再理一次:

示意圖

示意圖

方式2:使用Handler.post()

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

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

    });

// 步驟3:開啟工作線程(同時(shí)啟動(dòng)了Handler)
// 多線程可采用AsyncTask、繼承Thread類冲簿、實(shí)現(xiàn)Runnable
  • 源碼分析
    下面粟判,我將根據(jù)上述每個(gè)步驟進(jìn)行源碼分析

實(shí)際上,該方式與方式1中的Handler.sendMessage()工作原理相同峦剔、源碼分析類似档礁,下面將主要講解不同之處

步驟1:在主線程中創(chuàng)建Handler實(shí)例

/** 
  * 具體使用
  */
    private Handler mhandler = new  Handler();
    // 與方式1的使用不同:此處無復(fù)寫Handler.handleMessage()
 
/** 
  * 源碼分析:Handler的構(gòu)造方法
  * 作用:
  *     a. 在此之前羊异,主線程創(chuàng)建時(shí)隱式創(chuàng)建Looper對象事秀、MessageQueue對象
  *     b. 初始化Handler對象彤断、綁定線程 & 進(jìn)入消息循環(huán)
  * 此處的源碼分析類似方式1,此處不作過多描述
  */

步驟2:在工作線程中 發(fā)送消息到消息隊(duì)列中

/** 
  * 具體使用
  * 需傳入1個(gè)Runnable對象易迹、復(fù)寫run()從而指定UI操作
  */
    mHandler.post(new Runnable() {
            @Override
            public void run() {
                ... // 需執(zhí)行的UI操作 
            }

    });
 
/** 
  * 源碼分析:Handler.post(Runnable r)
  * 定義:屬于處理者類(Handler)中的方法
  * 作用:定義UI操作宰衙、將Runnable對象封裝成消息對象 & 發(fā)送 到消息隊(duì)列中(Message ->> MessageQueue)
  * 注:
  *    a. 相比sendMessage(),post()最大的不同在于睹欲,更新的UI操作可直接在重寫的run()中定義
  *    b. 實(shí)際上供炼,Runnable并無創(chuàng)建新線程,而是發(fā)送 消息 到消息隊(duì)列中
  */
  public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
           // getPostMessage(r) 的源碼分析->>分析1
           // sendMessageDelayed()的源碼分析 ->>分析2

        }
              /** 
               * 分析1:getPostMessage(r)
               * 作用:將傳入的Runable對象封裝成1個(gè)消息對象
               **/
              private static Message getPostMessage(Runnable r) {
                        // 1. 創(chuàng)建1個(gè)消息對象(Message)
                        Message m = Message.obtain();
                            // 注:創(chuàng)建Message對象可用關(guān)鍵字new 或 Message.obtain()
                            // 建議:使用Message.obtain()創(chuàng)建窘疮,
                            // 原因:因?yàn)镸essage內(nèi)部維護(hù)了1個(gè)Message池袋哼,用于Message的復(fù)用,使用obtain()直接從池內(nèi)獲取闸衫,從而避免使用new重新分配內(nèi)存

                        // 2. 將 Runable對象 賦值給消息對象(message)的callback屬性
                        m.callback = r;
                        
                        // 3. 返回該消息對象
                        return m;
                    } // 回到調(diào)用原處

             /** 
               * 分析2:sendMessageDelayed(msg, 0)
               * 作用:實(shí)際上涛贯,從此處開始,則類似方式1 = 將消息入隊(duì)到消息隊(duì)列蔚出,
               * 即 最終是調(diào)用MessageQueue.enqueueMessage()
               **/
               public final boolean sendMessageDelayed(Message msg, long delayMillis)
                {
                    if (delayMillis < 0) {
                        delayMillis = 0;
                    }

                    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
                    // 請看分析3
                }

             /** 
               * 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
               **/
               public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
                        // 1. 獲取對應(yīng)的消息隊(duì)列對象(MessageQueue)
                        MessageQueue queue = mQueue;

                        // 2. 調(diào)用了enqueueMessage方法 ->>分析3
                        return enqueueMessage(queue, msg, uptimeMillis);
                    }

             /** 
               * 分析4:enqueueMessage(queue, msg, uptimeMillis)
               **/
                private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
                     // 1. 將msg.target賦值為this
                     // 即 :把 當(dāng)前的Handler實(shí)例對象作為msg的target屬性
                     msg.target = this;
                     // 請回憶起上面說的Looper的loop()中消息循環(huán)時(shí)弟翘,會(huì)從消息隊(duì)列中取出每個(gè)消息msg,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
                     // 實(shí)際上則是將該消息派發(fā)給對應(yīng)的Handler實(shí)例        

                    // 2. 調(diào)用消息隊(duì)列的enqueueMessage()
                    // 即:Handler發(fā)送的消息骄酗,最終是保存到消息隊(duì)列
                    return queue.enqueueMessage(msg, uptimeMillis);
            }

            // 注:實(shí)際上從分析2開始稀余,源碼 與 sendMessage(Message msg)發(fā)送方式相同

從上面的分析可看出:

  • 消息對象的創(chuàng)建 = 內(nèi)部 根據(jù)Runnable對象而封裝;
  • 發(fā)送到消息隊(duì)列的邏輯 = 方式1中sendMessage(Message msg)趋翻。

下面睛琳,我們重新回到步驟1前的隱式操作2:消息循環(huán),即Looper類中的loop()方法

/** 
  * 源碼分析: Looper.loop()
  * 作用:消息循環(huán)踏烙,即從消息隊(duì)列中獲取消息师骗、分發(fā)消息到Handler
  * 特別注意:
  *       a. 主線程的消息循環(huán)不允許退出,即無限循環(huán)
  *       b. 子線程的消息循環(huán)允許退出:調(diào)用消息隊(duì)列MessageQueue的quit()
  */
  public static void loop() {
        
        ...// 僅貼出關(guān)鍵代碼

        // 1. 獲取當(dāng)前Looper的消息隊(duì)列
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // myLooper()作用:返回sThreadLocal存儲(chǔ)的Looper實(shí)例宙帝;若me為null 則拋出異常
            // 即loop()執(zhí)行前必須執(zhí)行prepare()丧凤,從而創(chuàng)建1個(gè)Looper實(shí)例
            
            final MessageQueue queue = me.mQueue;
            // 獲取Looper實(shí)例中的消息隊(duì)列對象(MessageQueue)

        // 2. 消息循環(huán)(通過for循環(huán))
            for (;;) {
            
            // 2.1 從消息隊(duì)列中取出消息
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            // next():取出消息隊(duì)列里的消息
            // 若取出的消息為空,則線程阻塞

            // 2.2 派發(fā)消息到對應(yīng)的Handler
            msg.target.dispatchMessage(msg);
            // 把消息Message派發(fā)給消息對象msg的target屬性
            // target屬性實(shí)際是1個(gè)handler對象
            // ->>分析1

        // 3. 釋放消息占據(jù)的資源
        msg.recycle();
        }
}

/** 
  * 分析1:dispatchMessage(msg)
  * 定義:屬于處理者類(Handler)中的方法
  * 作用:派發(fā)消息到對應(yīng)的Handler實(shí)例 & 根據(jù)傳入的msg作出對應(yīng)的操作
  */
  public void dispatchMessage(Message msg) {

    // 1. 若msg.callback屬性不為空步脓,則代表使用了post(Runnable r)發(fā)送消息(即此處需討論的)
    // 則執(zhí)行handleCallback(msg),即回調(diào)Runnable對象里復(fù)寫的run()->> 分析2
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 2. 若msg.callback屬性為空浩螺,則代表使用了sendMessage(Message msg)發(fā)送消息(即此處需討論的)
            // 則執(zhí)行handleMessage(msg)靴患,即回調(diào)復(fù)寫的handleMessage(msg) 
            handleMessage(msg);

        }
    }

  /** 
    * 分析2:handleCallback(msg)
    **/
    private static void handleCallback(Message message) {
        message.callback.run();
        //  Message對象的callback屬性 = 傳入的Runnable對象
        // 即回調(diào)Runnable對象里復(fù)寫的run()
    }

至此,你應(yīng)該明白使用 Handler.post()的工作流程:與方式1(Handler.sendMessage())類似要出,區(qū)別在于:

  1. 不需外部創(chuàng)建消息對象鸳君,而是內(nèi)部根據(jù)傳入的Runnable對象 封裝消息對象
  2. 回調(diào)的消息處理方法是:復(fù)寫Runnable對象的run()

二者的具體異同如下:

總結(jié)

至此,關(guān)于使用 Handler.post()的源碼解析完畢患蹂,總結(jié)如下:

示意圖

下面或颊,將順著文章:Android Handler:圖文解析 Handler通信機(jī)制 的工作原理再理一次:

示意圖

示意圖

至此砸紊,關(guān)于Handler機(jī)制的源碼全部分析完畢。


5. 總結(jié)

本文詳細(xì)分析了Handler機(jī)制的源碼囱挑,文字總結(jié) & 流程圖如下:

示意圖
示意圖
示意圖
示意圖

下一篇文章我將對講解Android Handler的相關(guān)知識醉顽,感興趣的同學(xué)可以繼續(xù)關(guān)注Carson_Ho的簡書

Anroid異步通信Handler系列文章
Android異步通信:Handler機(jī)制學(xué)習(xí)攻略
Android異步通信:Handler使用教程
Android異步通信:Handler工作原理
Android異步通信:Handler源碼分析
Android異步通信:詳解Handler內(nèi)存泄露的原因


歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨,追求短平挑、平游添、快,但卻不缺深度通熄。


請點(diǎn)贊唆涝!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唇辨,一起剝皮案震驚了整個(gè)濱河市廊酣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赏枚,老刑警劉巖亡驰,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嗡贺,居然都是意外死亡隐解,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門诫睬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來煞茫,“玉大人,你說我怎么就攤上這事摄凡⌒眨” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵亲澡,是天一觀的道長钦扭。 經(jīng)常有香客問我,道長床绪,這世上最難降的妖魔是什么客情? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮癞己,結(jié)果婚禮上膀斋,老公的妹妹穿的比我還像新娘。我一直安慰自己痹雅,他們只是感情好仰担,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绩社,像睡著了一般摔蓝。 火紅的嫁衣襯著肌膚如雪赂苗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天贮尉,我揣著相機(jī)與錄音拌滋,去河邊找鬼。 笑死绘盟,一個(gè)胖子當(dāng)著我的面吹牛鸠真,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播龄毡,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼吠卷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了沦零?” 一聲冷哼從身側(cè)響起祭隔,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎路操,沒想到半個(gè)月后疾渴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屯仗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年搞坝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魁袜。...
    茶點(diǎn)故事閱讀 38,018評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡桩撮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出峰弹,到底是詐尸還是另有隱情店量,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布鞠呈,位于F島的核電站融师,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蚁吝。R本人自食惡果不足惜旱爆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窘茁。 院中可真熱鬧疼鸟,春花似錦、人聲如沸庙曙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捌朴。三九已至吴攒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砂蔽,已是汗流浹背洼怔。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留左驾,地道東北人镣隶。 一個(gè)月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像诡右,于是被迫代替她去往敵國和親安岂。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評論 2 345

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