Android Handler源碼剖析

因?yàn)楣镜陌才牛悬c(diǎn)轉(zhuǎn)向后臺的工作了冷守,所以最近一直懶得寫Android的文章咱揍。但是秉著老本不能忘的原則颖榜,我今天為大家?guī)鞨andler的源碼剖析,往后的日子煤裙,源碼剖析的文章可能慢慢會(huì)多起來掩完,希望大家喜歡。

初識Handler

什么是Handler硼砰?從《Android開發(fā)藝術(shù)探索》里面且蓬,我總結(jié)了這么一段話:Android的消息機(jī)制主要是指Handler的運(yùn)行機(jī)制,Handler的運(yùn)行需要底層MessageQueue和Looper的支撐题翰。而在Android里恶阴,很多人認(rèn)為Handler的作用是更新UI,其實(shí)那只是他的一個(gè)特殊使用場景豹障,具體來說:有時(shí)候需要在子線程中進(jìn)行耗時(shí)的I/O操作冯事,可能是讀取文件或者訪問網(wǎng)絡(luò)等。

Handler的基本用法

Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what){
                case 1:

                    break;
            }
            return true;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain(handler,1);
                handler.sendMessageDelayed(message, 1000);
            }
        });
        thread.start();
    }

Handler源碼剖析

好了血公,看完了Handler的定義和基本用法之后昵仅,我們現(xiàn)在來看看我們的今天的重頭戲,我們要把Handler庖丁解牛一下累魔。
定義講得很清楚摔笤,Handler的機(jī)制涉及了另外三個(gè)重要的組件Looper、MessageQueue以及Message垦写。下面我們從Handler的構(gòu)造方式開始解析:

    public Handler(Callback callback, boolean async) {
        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());
            }
        }
        //(1)
        mLooper = Looper.myLooper();
        //(2)
        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;
    }

我們看到雖然構(gòu)造方法有很多個(gè)吕世,但是實(shí)際上有用的就只有一個(gè),下面我們來分析下:

(1)很快梯投,我們就在構(gòu)造方法里面看到一個(gè)熟悉的身影寞冯,那就是Looper渴析,我們點(diǎn)下去看下Looper.myLooper()做了什么晚伙;

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

其實(shí)就是利用sThreadLocal獲取到當(dāng)前的線程Looper吮龄;

(2)我們可以注意到,當(dāng)獲取的Looper是null的時(shí)候咆疗,程序就會(huì)報(bào)錯(cuò):Can't create handler inside thread that has not called Looper.prepare()漓帚,意思很簡單,就是說Handler的創(chuàng)建必須在Looper.prepare()之后執(zhí)行才能創(chuàng)建午磁。那接下來我們?nèi)タ纯碙ooper.prepare()方法究竟是何方神圣:

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

代碼很簡單尝抖,在(1)中我們看到了sThreadLocal.get()的方法獲取Looper,而Looper.prepare()里面是sThreadLocal.set(new Looper(quitAllowed));

顧名思義就是新建一個(gè)Looper迅皇,并且把它set進(jìn)去相應(yīng)的sThreadLocal中昧辽。

從(2)中,我們很容易就得出結(jié)論來:新建一個(gè)Handler必須在前先新建一個(gè)Looper登颓,且一個(gè)線程想要使用Handler搅荞,就必須得創(chuàng)建一個(gè)Looper對象。

得出這個(gè)結(jié)論之后框咙,有人會(huì)問咕痛,那主線程呢?主線程明明看上去沒有l(wèi)ooper喇嘱,為什么就能創(chuàng)建Handler呢茉贡?

答案在下面:

public static void main(String[] args) {

    ......

    Looper.prepareMainLooper();

    ......

    Looper.loop();

    ......

}

我們可以看到,在主線程的一個(gè)入口ActivityThread中的void main(String[] args)者铜,早已經(jīng)幫我們創(chuàng)建了一個(gè)looper腔丧,所以主線程里面可以隨便的新建Handler進(jìn)行使用。

好了作烟,看完Handler的構(gòu)造方法愉粤,我們就看下Handler的消息是怎么被發(fā)出的:

    //(1)
    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    //(2)
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
    //(3)
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    //(4)
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }
    //(5)
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
    //(6)
    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);
    }

我們可以看到(1)-(5)的發(fā)送消息方法都是指向方法(6),而方法(6)最關(guān)鍵是enqueueMessage()這個(gè)方法俗壹,我們接下來看看他究竟是何方神圣:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
    
    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) {
                // 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;
                //(1)
                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) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

(1)我們終于看到除了looper以外的第二個(gè)boss-MessageQueue科汗,消息隊(duì)列。我們可以看到在這個(gè)消息隊(duì)列關(guān)鍵代碼里面绷雏,有一個(gè)for的死循環(huán)头滔,而消息隊(duì)列的作用恰恰是兩個(gè):插入和讀取,而for循環(huán)中的next方法就是從隊(duì)列中取出一條Message消息并將其從消息隊(duì)列中移除涎显。另外坤检,從代碼上看,消息隊(duì)列并不是一個(gè)隊(duì)列期吓,而是一個(gè)單鏈表早歇。

有人會(huì)問,消息出來之后去了哪里呢,答案很明顯:Looper

下面我們看看Looper具體做了什么:

    public static void loop() {
        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;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            //(1)
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            ...

            msg.recycleUnchecked();
        }
    }

(1)我們看到又是一個(gè)for的死循環(huán)箭跳,而這個(gè)死循環(huán)的作用剛好是從消息隊(duì)列中queue.next()取出一條條Message晨另,然后將message調(diào)用msg.target.dispatchMessage(msg)方法發(fā)出去,這里的msg谱姓。target是發(fā)送這條消息的Handler對象借尿,這樣最終又交給Handler的dispatchMessage方法來處理了:

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

有人會(huì)問既然looper是死循環(huán),那么我們可以停掉它嗎屉来,答案是肯定的路翻。官方提供了兩個(gè)方法:

    public void quit() {
        mQueue.quit(false);
    }
    
    public void quitSafely() {
        mQueue.quit(true);
    }

一個(gè)是強(qiáng)制退出,另一個(gè)是安全退出茄靠,如果該looper退出后茂契,相應(yīng)的應(yīng)用也會(huì)被殺死。
好了慨绳,大概流程就說完了掉冶,下面給個(gè)圖,方便大家去更好的了解:

image

好了儡蔓,這期的源碼剖析就到這里了郭蕉,如果有什么地方說錯(cuò)的,可以向我指正喂江,大家共同進(jìn)步召锈!

我的掘金:
https://juejin.im/user/594e8e9a5188250d7b4cd875/posts

我的簡書:
http://www.reibang.com/u/b538ca57f640

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市获询,隨后出現(xiàn)的幾起案子涨岁,更是在濱河造成了極大的恐慌,老刑警劉巖吉嚣,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梢薪,死亡現(xiàn)場離奇詭異,居然都是意外死亡尝哆,警方通過查閱死者的電腦和手機(jī)秉撇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秋泄,“玉大人琐馆,你說我怎么就攤上這事『阈颍” “怎么了瘦麸?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長歧胁。 經(jīng)常有香客問我滋饲,道長厉碟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任屠缭,我火速辦了婚禮箍鼓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勿她。我一直安慰自己袄秩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布逢并。 她就那樣靜靜地躺著,像睡著了一般郭卫。 火紅的嫁衣襯著肌膚如雪砍聊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天贰军,我揣著相機(jī)與錄音玻蝌,去河邊找鬼。 笑死词疼,一個(gè)胖子當(dāng)著我的面吹牛俯树,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贰盗,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼许饿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舵盈?” 一聲冷哼從身側(cè)響起陋率,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎秽晚,沒想到半個(gè)月后瓦糟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赴蝇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年菩浙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片句伶。...
    茶點(diǎn)故事閱讀 37,997評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡劲蜻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出熄阻,到底是詐尸還是另有隱情斋竞,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布秃殉,位于F島的核電站坝初,受9級特大地震影響浸剩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鳄袍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一绢要、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拗小,春花似錦重罪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至阅束,卻和暖如春呼胚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背息裸。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工蝇更, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呼盆。 一個(gè)月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓年扩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親访圃。 傳聞我的和親對象是個(gè)殘疾皇子厨幻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評論 2 345

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