對Handler機制的理解

對Handler機制的理解

前言

Handeler機制算是Android中一個比較核心的內容了留凭,本文是經過對Handler源碼的分析做的一個總結耍休。

目錄

主要從以下幾個方面來總結:

  • Handler機制簡介
  • 核心類以及作用
  • Handler工作流程
  • 源碼分析

Handler機制簡介

Handler機制是一套消息傳遞機制蛤迎,在Android的多線程開發(fā)場景中會經常使用舔清,實現的功能是多線程之間的通信徐许。由于Android的UI操作不是線程安全的施蜜,所以在子線程中主要是通過Handler來對UI操作。

核心類以及作用

Handler機制由Handler雌隅、Looper翻默、MessageQueue和Message構成。

  • Handler

    Handler類兩個作用恰起,發(fā)送消息和處理消息修械。

  • MessageQueue

    MessageQueue用來維護消息隊列,根據時間將消息放入隊列和將消息移除隊列检盼。

  • Looper

    以死循環(huán)的方式不斷從MessageQueue中獲取消息肯污,分發(fā)消息。

  • Message

    消息的承載者

工作流程

工作流程主要分為4步:

  1. 核心類初始化

    為當前線程創(chuàng)建Looper梯皿,MessageQueue,即調用靜態(tài)方法Looper.prepare(),然后創(chuàng)建Handler對象(有多個重載的構造方法仇箱,可以指定Looper,默認是當前線程的Looper)东羹,啟動循環(huán)Looper.loop().

  2. 發(fā)送消息

    通過Handler的sendMessage和post方法發(fā)送消息,將消息加入到消息隊列剂桥。

  3. 消息循環(huán)

    Lopper不停通過循環(huán),從MessageQueue中拿到Message属提,然后調用與Message綁定的Handler來分發(fā)消息(handler.dispatchMessage(Message msg))权逗,具體分發(fā)邏輯會在后文講到。

  4. 消息處理

    Handler的dispatchMessage方法被調用后冤议,會選擇執(zhí)行具體的消息處理邏輯斟薇。

注意事項:Thread、Handler恕酸、MessageQueue和Looper的對應關系堪滨。


  • 一個Thread只能綁定一個Looper,一個MessageQueue蕊温,多個Handler袱箱。
  • 一個Looper只能綁定一個MessageQueue,多個Handler
  • 一個Handler只能綁定一個Looper和一個MessageQueque

源碼分析

這一部分主要是分析核心類的構造方法和主要方法义矛,來提高對Handler機制細節(jié)方面的認識发笔。

Looper類

先看構造方法:

    //私有構造方法,參數表示是否允許退出循環(huán)凉翻,
   private Looper(boolean quitAllowed) {
        //創(chuàng)建消息隊列了讨,保存當前線程,參數由MessageQueue接收,可見退出邏輯應該在MessageQueue中實現
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
   }
   //子線程Looper初始化
   public static void prepare() {
        //子線程允許退出
        prepare(true);
    }
    //在此方法中創(chuàng)建Looper
   private static void prepare(boolean quitAllowed) {
        //檢查當前線程是否已有Looper前计,每個線程只能執(zhí)行一次胞谭,否則將會拋出異常,即一個線程只能有一個Looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //創(chuàng)建Looper男杈,并保存在當前線程中
        sThreadLocal.set(new Looper(quitAllowed));
    }
   //主線程Looper初始化
   public static void prepareMainLooper() {
        //主線程不允許退出
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            //保存主線程Looper
            sMainLooper = myLooper();
        }
    }
    //獲取當前線程的Looper
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    //判斷當前線程是不是運行在與Looper關聯(lián)的線程中
    public boolean isCurrentThread() {
        return Thread.currentThread() == mThread;
    }
    //在任何地方都可通過此方法獲取主線程的Looper對象
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }


上面列出了Looper的構造方法以及和構造方法相關的幾個方法韭赘,由分析可知:

  • Looper的構造方法是私有的,只能通過靜態(tài)方法*prepare()和prepareMainLooper()為當前線程創(chuàng)建Looper势就。
  • 創(chuàng)建Looper的時候就會創(chuàng)建MessageQueue泉瞻,并綁定MessageQueue和Thread實例。
  • 主線程不允許退出苞冯,子線程可以退出袖牙,退出邏輯在MessageQueue中實現。
  • 創(chuàng)建的Looper實例通過ThreadLocal被保存在當前線程中舅锄。(需要知道:ThreadLocal可以將對象保存在當前線程中鞭达,并且也可以從當前線程中獲取之前保存的對象,至于具體實現這里不在贅述

循環(huán)方法分析:


    public static void loop() {
        //這里要注意prepare()和loop()的調用順序皇忿,以及要確定在調用此方法的線程中是否有Looper,否則會拋出異常       
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //拿到消息隊列
        final MessageQueue queue = me.mQueue;

        //開始循環(huán)讀取消息隊列中的消息
        for (;;) {
            //在隊列中沒有消息時畴蹭,會阻塞線程
            Message msg = queue.next(); // might block
            //退出消息循環(huán)的時機是在消息隊列返回Null值時
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            try {
                //將消息交給與消息綁定的handler處理,也就是發(fā)送這條消息的Handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            //回收消息
            msg.recycleUnchecked();
        }
    }
    

Handler類

從以下三個方面來分析源碼:

  • 構造方法
  • 發(fā)送消息
  • 處理消息

構造方法

    public Handler() {
        this(null, false);
    }
    
    public Handler(Callback callback) {
        this(callback, false);
    }
    
    public Handler(boolean async) {
        this(null, async);
    }
    
    public Handler(Looper looper) {
        this(looper, null, false);
    }
        
    public Handler(Callback callback, boolean async) {
        //繼承Handler時鳍烁,若沒有用static修飾叨襟,可能會造成內存泄漏
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        //在子線程中直接使用new Handler()會拋出異常的原因:沒有創(chuàng)建Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
     public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
    /**
    *@param looper 指定與handler綁定的Looper
    *@param callback Handler中定義的接口,只有一個handleMessage(Message msg)方法
    *@param async 只有系統(tǒng)創(chuàng)建Handler會用到這個參數幔荒,表示由此Handler發(fā)送的消息是否要標記為異步消息
    */
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

從上面可以知道糊闽,Handler的構造方法有很多個重載,但是我們能調用的方法只有三個爹梁,其他的都被用@hide注解右犹,只有sdk能調用。

  • Handler()
  • Handler(Callback callback)
  • Handler(Looper looper)

通過構造方法可以總結兩點:

  • 在繼承Handler時姚垃,需要注意可能發(fā)生的內存泄漏
  • 子線程在沒有調用Looper.prepare()時念链,除了Handler(Looper looper),其他兩個構造方法不能調用积糯,否則會拋出異常掂墓。

發(fā)送消息

發(fā)送消息有兩種方式

  1. sendMessage:發(fā)送一個Message對象
  2. post:發(fā)送一個Runnable,Runnable最終會被包裝成Message對象
    //包裝Runnable
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }
    //消息最終都是通過這個方法發(fā)送
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    //直接將消息放在消息隊列頭部絮宁,優(yōu)先處理梆暮,可能有副作用
    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, 0);
    }
    //將消息加入到消息隊列中
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //將消息與當前Handler綁定
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    

處理消息

    //分發(fā)消息服协,由Looper調用
    public void dispatchMessage(Message msg) {
        //先判斷是否是post的消息绍昂,
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //在判斷當前Handler是否設置CallBack,構造方法有說到
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后才調用Handler的成員方法
            handleMessage(msg);
        }
    }
MessageQueue類

MessageQueue用于維護消息隊列,主要有兩種操作:

  1. 將消息加入到消息隊列
  1. 將消息移除消息隊列

將消息加入到消息隊列:


boolean enqueueMessage(Message msg, long when) {
        ...省略
        synchronized (this) {
            ...省略

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                //當前是空隊列or消息是通過sendMessageAtFrontOfQueue發(fā)送或時間小于隊列第一個消息的時間窘游,則將msg置于隊列頭部
                //若當前是阻塞狀態(tài)唠椭,需要喚醒線程
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //死循環(huán),按時間順序忍饰,將消息放入隊列
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    //時間大于隊列中的消息贪嫂,并且消息是異步的,不需要喚醒
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
            //調用native方法艾蓝,喚醒線程
                nativeWake(mPtr);
            }
        }
        return true;
    }
    

注意:消息隊列雖然是一個隊列力崇,但并不完全遵循先入先出的規(guī)則,消息在入隊時赢织,會遍歷鏈表亮靴,比較每一個消息的時間,按時間排序

將消息移除消息隊列:


Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //
        int nextPollTimeoutMillis = 0;
        //死循環(huán)
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //nextPollTimeoutMillis不為0時于置,阻塞線程
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                        //這里也有一個循環(huán)茧吊,用來找到下一個要處理的message
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //當前時間小于下一個消息的時間,計算阻塞時間
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //從隊列中取出消息
                        // Got a message.
                        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 {
                        //沒有消息時線程阻塞
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                    //若當前處于退出狀態(tài)八毯,返回null
                if (mQuitting) {
                    dispose();
                    return null;
                }

               ...省略
        }
    }

至此搓侄,對Handler機制的源碼分析完畢,通過閱讀這些源碼话速,對Handler機制的實現細節(jié)有了一個更深的認識讶踪。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泊交,隨后出現的幾起案子俊柔,更是在濱河造成了極大的恐慌,老刑警劉巖活合,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雏婶,死亡現場離奇詭異,居然都是意外死亡白指,警方通過查閱死者的電腦和手機留晚,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來告嘲,“玉大人错维,你說我怎么就攤上這事¢匣#” “怎么了赋焕?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仰楚。 經常有香客問我隆判,道長犬庇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任侨嘀,我火速辦了婚禮臭挽,結果婚禮上,老公的妹妹穿的比我還像新娘咬腕。我一直安慰自己欢峰,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布涨共。 她就那樣靜靜地躺著纽帖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪举反。 梳的紋絲不亂的頭發(fā)上抛计,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音照筑,去河邊找鬼吹截。 笑死,一個胖子當著我的面吹牛凝危,可吹牛的內容都是我干的波俄。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼蛾默,長吁一口氣:“原來是場噩夢啊……” “哼懦铺!你這毒婦竟也來了?” 一聲冷哼從身側響起支鸡,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤冬念,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后牧挣,有當地人在樹林里發(fā)現了一具尸體急前,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年瀑构,在試婚紗的時候發(fā)現自己被綠了裆针。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡寺晌,死狀恐怖世吨,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情呻征,我是刑警寧澤耘婚,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站陆赋,受9級特大地震影響沐祷,放射性物質發(fā)生泄漏嚷闭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一戈轿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧阵子,春花似錦思杯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至领突,卻和暖如春暖璧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背君旦。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工澎办, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人金砍。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓局蚀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親恕稠。 傳聞我的和親對象是個殘疾皇子琅绅,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容