Android中的handler源碼解析(超詳細(xì))

Handler是什么?
Android中的異步消息處理機(jī)制,使用者可以在不阻塞UI線程的前提下輕松的實(shí)現(xiàn)消息管理和發(fā)送。

原理:

handler機(jī)制中包含4個(gè)關(guān)鍵類(下面對(duì)源碼的解析也是從這4個(gè)類入手),Message(消息),MessageQueue(消息隊(duì)列),Looper(輪詢器),Handler(消息發(fā)送和接收并處理),簡(jiǎn)單一句話概括就是:handler負(fù)責(zé)發(fā)送message,將其加入到MessageQueue中,Looper不間斷的從MessageQueue中取出消息,并發(fā)送給對(duì)應(yīng)的handler實(shí)例去處理。

重點(diǎn):源碼解析

有些同學(xué)不知道如何去看源碼,這就很尷尬了,往往想知道系統(tǒng)里面的源碼或者牛逼的開源項(xiàng)目是如何設(shè)計(jì)的,但是就是不知道如何入手,還有些同學(xué)覺得看源碼沒用,懂得怎么用不就行了嗎,非也,懂得如何用那只是招式,可能換個(gè)地方換種形式你就不認(rèn)識(shí)了,懂源碼,那是心法,知其所以然,才能千變?nèi)f化!

首先,我們使用的時(shí)候是這樣的:

Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                 //消息處理
                  ·········
                }
            };
Message msg = Message.obtain();
handler.sendMessage(Message msg);

定義一個(gè)handler,并重寫了他的handleMessage()方法;我們來看看它的構(gòu)造方法:

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

public Handler(Callback callback) {
    this(callback, false);
}
 
 public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

public Handler(Looper looper, Callback callback) {
     this(looper, callback, false);
 }

@hide
public Handler(boolean async) {
     this(null, async);
}

@hide
public Handler(Callback callback, boolean async) {
    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;
 }

@hide
public Handler(Looper looper, Callback callback, boolean async) {
     mLooper = looper;
     mQueue = looper.mQueue;
     mCallback = callback;
     mAsynchronous = async;
 }

蒙蔽了,這么多!但是注意,后面三個(gè)你都是沒法使用的,因?yàn)橛蠤hide;
先看空參構(gòu)造,他直接調(diào)用了Handler(Callback callback, boolean async);看看里面做了什么:

  1. 獲取looper對(duì)象:Looper.myLooper(); 進(jìn)去可以看到 返回了,sThreadLocal.get();這個(gè)是什么玩意?ThreadLocal,他是一個(gè)容器,里面封裝了一個(gè)map,以當(dāng)前線程的ThreadLocal作為key,以你要存的值作為value,我們這里value就是looper對(duì)象,這個(gè)容器是專門用來保存線程所特有的變量的,起到了線程間隔離的作用,如果有人想知道,我專門開一篇博客來講。至于looper是怎么初始化的,客官莫急,下面會(huì)講。
  2. mQueue = mLooper.mQueue;拿到消息隊(duì)列,這是一個(gè)鏈表結(jié)構(gòu),注:一個(gè)線程對(duì)應(yīng)一個(gè)looper,一個(gè)looper同樣也只有一個(gè)queue;
  3. callback這個(gè)是handler里面的一個(gè)接口,在消息分發(fā)的時(shí)候會(huì)講到,默認(rèn)是null,mAsynchronous這個(gè)參數(shù)表示消息是否是異步消息,默認(rèn)是false;

接下來看Message.obtain():

public static Message obtain() {
        synchronized (sPoolSync) { //同步代碼塊
            if (sPool != null) {
                Message m = sPool;    //sPool是一個(gè)靜態(tài)的Message 引用
                sPool = m.next;       //next也是Message,但是他不是靜態(tài)的;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

obtain()的實(shí)現(xiàn)非常有意思,我們知道m(xù)essage其實(shí)是復(fù)用的,message中有一個(gè)方法:recycle(),大家可以去看一下,消息被處理完畢后會(huì)調(diào)用recycle()方法,將message還原,并將sPool這個(gè)賦值為this,也就是當(dāng)前自己的實(shí)例對(duì)象,如果sPool是null那么當(dāng)前沒有消息可以復(fù)用,直接new出來并返回,如果不是null,那么將當(dāng)前的sPool返回,那么這個(gè)next又是干什么的呢?代碼可見,sPool =m.next;將sPool重新賦值,這個(gè)m.next就是一條將要處理的消息,也就是說每一個(gè)msg里面都有對(duì)下一個(gè)將要處理的消息的引用,這樣,sPool被賦值了,下次再執(zhí)行obtain()的時(shí)候,sPool指向的其實(shí)是另外一個(gè)Message實(shí)例了,至于next如何賦值,稍后在MessageQueue中詳細(xì)解析。

Message拿到了,開始發(fā)消息吧,sendMessage(Message msg);

public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);   //直接調(diào)用sendMessageDelayed方法
}

通過源碼,一層一層往下找,發(fā)現(xiàn):sendMessage ——> sendMessageDelayed ——>sendMessageAtTime

  public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  //此方法才是真正發(fā)消息
        MessageQueue queue = mQueue;    //先拿消息隊(duì)列
        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);
    }

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;      //重點(diǎn):this表示的是當(dāng)前的handler實(shí)例,每個(gè)message都會(huì)記錄發(fā)送它的那個(gè)handler
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);      //這里調(diào)用了messageQueue的方法;
    }
重點(diǎn):為什么msg要保存handler的引用呢?

我們都知道,handler機(jī)制消息處理,哪一個(gè)handler實(shí)例發(fā)出的消息,那么那一個(gè)handler就負(fù)責(zé)處理這個(gè)消息,這里的msg.target = this; 作用就在這,用來記住誰將msg發(fā)出來,等到處理的時(shí)候誰就來處理。

接著分析,handler最后調(diào)用了queue.enqueueMessage(msg, uptimeMillis);
我們來看看這個(gè)方法:(比較多,我精簡(jiǎn)了一下,留下重要的部分)

    boolean enqueueMessage(Message msg, long when) {
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;//當(dāng)前將要被處理的msg
            boolean needWake;
            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;
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

mMessages是即將要處理的message,先看第一個(gè)if最有一個(gè)條件,when<p.when,這個(gè)when是什么呢,它是一個(gè)時(shí)間毫秒值,when = 發(fā)送消息的那一刻的時(shí)間+delytime(延后的時(shí)間);表示這個(gè)msg將要在什么時(shí)刻可以被處理,when越小,越要被有先處理,所以,if判斷新來的msg是否優(yōu)先級(jí)比當(dāng)前即將要被處理的高,如果是,那么將此消息放在第一位(mMessages = msg),并且還做了另外一件事,msg.next = p; p下一個(gè)要處理的msg,else里面道理也是一樣的,for循環(huán)比較

重點(diǎn)又來了! Looper!
上面留下了一個(gè)問題,Looper是怎么初始化的呢,app中的通信,UI的刷新,都需要依賴handler,那么,我們就猜想,Looper在app啟動(dòng)的時(shí)候就已經(jīng)開始創(chuàng)建并初始化了,那么我們?nèi)ピ创a中找 ActivityThread main()方法 ;

public static void main(String[] args) {
        ````````
        Looper.prepareMainLooper();    //在這里!

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }
        Looper.loop();  //還有這里
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

果然,在主線程啟動(dòng)的時(shí)候就已經(jīng)啟動(dòng)了Looper并調(diào)用了,prepareMainLooper()和loop();接著去looper里面看看:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

 private static void prepare(boolean quitAllowed) {
  //判斷l(xiāng)ooper是否是null如果是,就創(chuàng)建,并將其存到ThreadLocal中,上面說的handler中的looper就是從ThreadLocal中取出來的;
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    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;

        Binder.clearCallingIdentity();   //這里大家不用管,我個(gè)人理解是對(duì)進(jìn)程的校驗(yàn),有知道的同學(xué)也可以留言告訴我。
        final long ident = Binder.clearCallingIdentity();

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

            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);    //分發(fā)消息
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();      //消息的回收
        }
    }

三個(gè)方法,prepareMainLooper()其實(shí)內(nèi)部調(diào)用了prepare(false);這個(gè)boolean參數(shù)是什么意思呢,意識(shí)就是不允許messageQueue退出,這個(gè)參數(shù)會(huì)在構(gòu)建Queue的時(shí)候傳遞進(jìn)去,因?yàn)橹骶€程的消息隊(duì)列只有在應(yīng)用退出的時(shí)候才允許退出,否則······沒有否則,消息機(jī)制都沒了,還怎么玩!
prepare()方法創(chuàng)建了一個(gè)looper,但是looper并沒有啟動(dòng),啟動(dòng)的方法是下面的loop();loop()有一個(gè)for(;;),死循環(huán),里面做的工作就是一直取消息,并處理,然后recycleUnchecked()復(fù)用。這個(gè)時(shí)候looper就啟動(dòng)起來了,只要你的應(yīng)用還在運(yùn)行,他就會(huì)一直在。looper拿到消息后會(huì)通過msg.target.dispatchMessage(msg); 將消息發(fā)出來給handler處理,這里的msg.target不就是我們發(fā)消息的時(shí)候初始化的handler嗎?(篇幅有點(diǎn)長(zhǎng),不記得可以往上翻)

繼續(xù)看,又到重點(diǎn)了

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

通過一系列判斷,來處理消息,如下:

  1. 判斷msg.callback是不是null,如果不是,那么就給這個(gè)callback處理,這個(gè)callback是message中的一個(gè)Runnable;Message.obtain()其實(shí)是有其他參數(shù)的方法的,其中有一個(gè)是obtain(Handler h, Runnable callback);如果你用了這個(gè),那么消息就會(huì)在你實(shí)現(xiàn)的Runnable中接收到處理的回調(diào);
  2. 第二個(gè)是判斷handler內(nèi)部的callback是不是null,如果不是null,就讓他去處理,這里的Callback可不是Runnable了,他是一個(gè)interface,里面定義了一個(gè)handleMessage(Message msg);方法,這個(gè)怎么實(shí)現(xiàn)呢?handler類里同樣有Handler(Callback callback)構(gòu)造方法
  3. 最后才輪到handler類里的方法handleMessage來處理消息。也就是我們?cè)陂_始寫的那個(gè)簡(jiǎn)單的handler使用方法。
結(jié)語:本文從使用過程為主線,來分析Handler原理,文中含有大量篇幅的源碼,也比較詳細(xì),網(wǎng)上其他的博客也很多,但是講的都比較粗糙,講解的流程也不容易理解,無法串聯(lián)起來,希望通過這篇博客能讓大家完全理解Handler機(jī)制,若有錯(cuò)誤的地方,歡迎指正!
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,694評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,690評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,019評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評(píng)論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,188評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,718評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,438評(píng)論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,667評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,845評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評(píng)論 1 295
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,384評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評(píng)論 2 380

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