真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

從架構(gòu)師的角度分析AndroidHandler源碼的正確姿勢

Handler的原理是什么?能深入分析下 Handler的實現(xiàn)機(jī)制嗎?
面試官問該問題是想問清楚handler的源碼,handler機(jī)制如何實現(xiàn),對消息泵Looper理不理解
(更多完整項目下載。未完待續(xù)。源碼。圖文知識后續(xù)上傳github。)

創(chuàng)新互聯(lián)公司堅信:善待客戶,將會成為終身客戶。我們能堅持多年,是因為我們一直可值得信賴。我們從不忽悠初訪客戶,我們用心做好本職工作,不忘初心,方得始終。10年網(wǎng)站建設(shè)經(jīng)驗創(chuàng)新互聯(lián)公司是成都老牌網(wǎng)站營銷服務(wù)商,為您提供成都網(wǎng)站制作、做網(wǎng)站、網(wǎng)站設(shè)計、H5響應(yīng)式網(wǎng)站、網(wǎng)站制作、品牌網(wǎng)站建設(shè)、微信小程序定制開發(fā)服務(wù),給眾多知名企業(yè)提供過好品質(zhì)的建站服務(wù)。

從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢

1. Handler 機(jī)制簡介

  • 定義 一套 Android 消息傳遞機(jī)制
  • 作用
    從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢

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

使用Handler的原因:將工作線程需操作UI的消息 傳遞 到主線程,使得主線程可根據(jù)工作線程的需求 更新UI,從而避免線程操作不安全的問題

2. 儲備知識

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

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

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

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

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

從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢
在源碼分析前,先來了解Handler機(jī)制中的核心類

3.1 類說明

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

  • 處理器 類(Handler)
  • 消息隊列 類(MessageQueue)
  • 循環(huán)器 類(Looper)
    3.2Handler工作流程

    從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢

4.源碼分析

從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢

  • 下面的源碼分析將根據(jù) Handler的使用步驟進(jìn)行

  • Handler使用方式 因發(fā)送消息到消息隊列的方式不同而不同,共分為2種:使用Handler.sendMessage()、使用Handler.post()

  • 下面的源碼分析將依據(jù)上述2種使用方式進(jìn)行
方式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(); // 實例化消息對象
    msg.what = 1; // 消息標(biāo)識
    msg.obj = "AA"; // 消息內(nèi)容存放

  // 步驟3:在工作線程中 通過Handler發(fā)送消息到消息隊列中
  // 多線程可采用AsyncTask、繼承Thread類、實現(xiàn)Runnable
   mHandler.sendMessage(msg);

  // 步驟4:開啟工作線程(同時啟動了Handler)
  // 多線程可采用AsyncTask、繼承Thread類、實現(xiàn)Runnable
  • 源碼分析 下面,我將根據(jù)上述每個步驟進(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的消息處理會在綁定的線程中執(zhí)行
  *   b. 綁定方式 = 先指定Looper對象,從而綁定了 Looper對象所綁定的線程(因為Looper對象本已綁定了對應(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. 綁定消息隊列對象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 獲取該Looper對象中保存的消息隊列對象(MessageQueue)
                // 至此,保證了handler對象 關(guān)聯(lián)上 Looper對象中MessageQueue
    }
  • 從上面可看出: 當(dāng)創(chuàng)建Handler對象時,則通過 構(gòu)造方法 自動關(guān)聯(lián)當(dāng)前線程的Looper對象 & 對應(yīng)的消息隊列對象(MessageQueue),從而 自動綁定了 實現(xiàn)創(chuàng)建Handler對象操作的線程
    0 那么,當(dāng)前線程的Looper對象 & 對應(yīng)的消息隊列對象(MessageQueue) 是什么時候創(chuàng)建的呢?

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

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

從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢
從架構(gòu)師的角度分析Android Handler 源碼的正確姿勢

  • 源碼分析
/** 
  * 源碼分析1:Looper.prepare()
  * 作用:為當(dāng)前線程(子線程) 創(chuàng)建1個循環(huán)器對象(Looper),同時也生成了1個消息隊列對象(MessageQueue)
  * 注:需在子線程中手動調(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個線程中只能對應(yīng)1個Looper實例
        // 注:sThreadLocal = 1個ThreadLocal對象,用于存儲線程的變量

        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個消息隊列對象(MessageQueue)
            // 即 當(dāng)創(chuàng)建1個Looper實例時,會自動創(chuàng)建一個與之配對的消息隊列對象(MessageQueue)

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

/** 
  * 源碼分析2:Looper.prepareMainLooper()
  * 作用:為 主線程(UI線程) 創(chuàng)建1個循環(huán)器對象(Looper),同時也生成了1個消息隊列對象(MessageQueue)
  * 注:該方法在主線程(UI線程)創(chuàng)建時自動調(diào)用,即 主線程的Looper對象自動生成,不需手動生成
  */
    // 在Android應(yīng)用進(jìn)程啟動時,會默認(rèn)創(chuàng)建1個主線程(ActivityThread,也叫UI線程)
    // 創(chuàng)建時,會自動調(diào)用ActivityThread的1個靜態(tài)的main()方法 = 應(yīng)用程序的入口
    // main()內(nèi)則會調(diào)用Looper.prepareMainLooper()為主線程生成1個Looper對象

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

            Looper.prepareMainLooper(); 
            // 1. 為主線程創(chuàng)建1個Looper對象,同時生成1個消息隊列對象(MessageQueue)
            // 方法邏輯類似Looper.prepare()
            // 注:prepare():為子線程中創(chuàng)建1個Looper對象

            ActivityThread thread = new ActivityThread(); 
            // 2. 創(chuàng)建主線程

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

        }
總結(jié):
  • 創(chuàng)建主線程時,會自動調(diào)用ActivityThread的1個靜態(tài)的main();而main()內(nèi)則會調(diào)用Looper.prepareMainLooper()為主線程生成1個Looper對象,同時也會生成其對應(yīng)的MessageQueue對象

    1.即 主線程的Looper對象自動生成,不需手動生成;而子線程的Looper對象則需手動通過Looper.prepare()創(chuàng)建
    2.在子線程若不手動創(chuàng)建Looper對象 則無法生成Handler對象

  • 根據(jù)Handler的作用(在主線程更新UI),故Handler實例的創(chuàng)建場景 主要在主線程
  • 生成Looper& MessageQueue對象后,則會自動進(jìn)入消息循環(huán):Looper.loop(),即又是另外一個隱式操作。
    步驟1前的隱式操作2:消息循環(huán)

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

/**

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

    ...// 僅貼出關(guān)鍵代碼
    
    // 1. 獲取當(dāng)前Looper的消息隊列
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        // myLooper()作用:返回sThreadLocal存儲的Looper實例;若me為null 則拋出異常
        // 即loop()執(zhí)行前必須執(zhí)行prepare(),從而創(chuàng)建1個Looper實例
    
        final MessageQueue queue = me.mQueue;
        // 獲取Looper實例中的消息隊列對象(MessageQueue)
    
    // 2. 消息循環(huán)(通過for循環(huán))
        for (;;) {
    
        // 2.1 從消息隊列中取出消息
        Message msg = queue.next(); 
        if (msg == null) {
            return;
        }
        // next():取出消息隊列里的消息
        // 若取出的消息為空,則線程阻塞
        // ->> 分析1 
    
        // 2.2 派發(fā)消息到對應(yīng)的Handler
        msg.target.dispatchMessage(msg);
        // 把消息Message派發(fā)給消息對象msg的target屬性
        // target屬性實際是1個handler對象
        // ->>分析2
    
    // 3. 釋放消息占據(jù)的資源
    msg.recycle();
    }

    }

/**

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

    ...// 僅貼出關(guān)鍵代碼
    
    // 該參數(shù)用于確定消息隊列中是否還有消息
    // 從而決定消息隊列應(yīng)處于出隊消息狀態(tài) or 等待狀態(tài)
    int nextPollTimeoutMillis = 0;
    
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
    
    // nativePollOnce方法在native層,若是nextPollTimeoutMillis為-1,此時消息隊列處于等待狀態(tài) 
    nativePollOnce(ptr, nextPollTimeoutMillis);
    
    synchronized (this) {
    
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;
    
        // 出隊消息,即 從消息隊列中取出消息:按創(chuàng)建Message對象的時間順序
        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 {
    
            // 若 消息隊列中已無消息,則將nextPollTimeoutMillis參數(shù)設(shè)為-1
            // 下次循環(huán)時,消息隊列則處于等待狀態(tài)
            nextPollTimeoutMillis = -1;
        }
    
        ......
    }
       .....

    }
    }// 回到分析原處

/**

  • 分析2:dispatchMessage(msg)
  • 定義:屬于處理者類(Handler)中的方法
  • 作用:派發(fā)消息到對應(yīng)的Handler實例 & 根據(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é)論會在講解使用“post(Runnable r)”方式時講解
    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實例時復(fù)寫 = 自定義消息處理方式
      **/
      public void handleMessage(Message msg) {
      ... // 創(chuàng)建Handler實例時復(fù)寫
      }

      
      ### 總結(jié):
  • 消息循環(huán)的操作 = 消息出隊 + 分發(fā)給對應(yīng)的Handler實例

  • 分發(fā)給對應(yīng)的Handler的過程:根據(jù)出隊消息的歸屬者通過dispatchMessage(msg)進(jìn)行分發(fā),最終回調(diào)復(fù)寫的handleMessage(Message msg),從而實現(xiàn) 消息處理 的操作

  • 特別注意:在進(jìn)行消息分發(fā)時
(dispatchMessage(msg))

,會進(jìn)行1次發(fā)送方式的判斷:

msg.callback屬性不為空,則代表使用了post(Runnable r)發(fā)送消息,則直接回調(diào)Runnable對象里復(fù)寫的run()
msg.callback屬性為空,則代表使用了sendMessage(Message msg)發(fā)送消息,則回調(diào)復(fù)寫的handleMessage(msg)
至此,關(guān)于步驟1的源碼分析講解完畢

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

/** 
  * 具體使用
  */
    Message msg = Message.obtain(); // 實例化消息對象
    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個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ā)送消息到消息隊列中

多線程的實現(xiàn)方式:AsyncTask、繼承Thread類、實現(xiàn)Runnable

/** 
  * 具體使用
  */

    mHandler.sendMessage(msg);

/** 
  * 源碼分析:mHandler.sendMessage(msg)
  * 定義:屬于處理器類(Handler)的方法
  * 作用:將消息 發(fā)送 到消息隊列中(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)的消息隊列對象(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實例對象作為msg的target屬性
                 msg.target = this;
                 // 請回憶起上面說的Looper的loop()中消息循環(huán)時,會從消息隊列中取出每個消息msg,然后執(zhí)行msg.target.dispatchMessage(msg)去處理消息
                 // 實際上則是將該消息派發(fā)給對應(yīng)的Handler實例        

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

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

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

                synchronized (this) {

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

                    // 判斷消息隊列里有無消息
                        // a. 若無,則將當(dāng)前插入的消息 作為隊頭 & 若此時消息隊列處于等待狀態(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. 判斷消息隊列里有消息,則根據(jù) 消息(Message)創(chuàng)建的時間 插入到隊列中
                            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)
// 不斷從消息隊列中取出Handler發(fā)送的消息 & 分發(fā)到對應(yīng)Handler
// 最終回調(diào)Handler.handleMessage()處理消息

(更多完整項目下載。未完待續(xù)。源碼。圖文知識后續(xù)上傳github。)


文章標(biāo)題:從架構(gòu)師的角度分析AndroidHandler源碼的正確姿勢
網(wǎng)頁網(wǎng)址:http://weahome.cn/article/ihpioj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部