Android Handler源碼淺析

Android Handler源碼淺析

相關(guān)對(duì)象

  1. Handler可以看作CEO碟婆,負(fù)責(zé)消息的處理和發(fā)送吠勘,Handler發(fā)送消息給MessageQueue,涵防,然后Looper取出其中的消息給Handler铜幽。
  2. Looper,Handler的管家驴剔,可以看作秘書抄沮,負(fù)責(zé)管理MessageQueue,她會(huì)不斷的從MessageQueue中取出消息跋核,交給Handler處理岖瑰。
  3. MessageQueue是存放消息的隊(duì)列,負(fù)責(zé)存放Handler發(fā)來(lái)的消息了罪。

Looper部分源碼講解

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));
}
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

Looper實(shí)例化是在Looper.prepare()方法中锭环。

小插曲

ThreadLocal是一個(gè)和多線程并發(fā)相關(guān)的類。當(dāng)使用ThreadLocal維護(hù)變量時(shí)泊藕,ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本辅辩,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本娃圆。

ThreadLocal維護(hù)了一個(gè)Map集合玫锋,其中鍵為當(dāng)前線程,值就是不同線程的變量讼呢。

ThreadLocal中的方法不多撩鹿。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null)
            return (T)e.value;
    }
    return setInitialValue();
}
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

set方法很容易理解,首先獲取當(dāng)前線程悦屏,再用當(dāng)前線程當(dāng)鍵來(lái)獲取map集合节沦,如果map為空,則重新創(chuàng)建一個(gè)map并設(shè)置值進(jìn)去础爬,否則的話甫贯,直接設(shè)置值進(jìn)去。get同理看蚜。

回到主題叫搁。所以Looper的prepare只能獲取當(dāng)前線程的Looper對(duì)象,并且如果已經(jīng)存在一個(gè)Looper對(duì)象則會(huì)報(bào)異常供炎,所以這也說(shuō)明了一個(gè)線程中只能存在一個(gè)Looper對(duì)象渴逻。并且prepare只能調(diào)用一次。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
public static void loop() {
    final Looper me = myLooper();
    //此處只展示了關(guān)鍵代碼
    final MessageQueue queue = me.mQueue;

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        msg.recycleUnchecked();
    }
}

Looper中創(chuàng)建了一個(gè)MessageQueue音诫,并且在Looper的loop方法中惨奕,開啟無(wú)限循環(huán)去遍歷MessageQueue,如果消息不為空竭钝,則將消息發(fā)送給Handler梨撞。這個(gè)msg.target其實(shí)就是Handler對(duì)象。

我們一般都會(huì)重寫Handler的handleMessage方法蜓氨,其中緣由也在源碼里聋袋。
我們不管sendEmptyMessage也好,postDelayed也罷穴吹,最終都會(huì)調(diào)用到一個(gè)方法幽勒。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

msg.target = this。這句話中的this就是Handler對(duì)象港令。
同時(shí)把消息放入MessageQueue中啥容。

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

在Looper的loop方法中锈颗,她把消息用dispatchMessage傳了過(guò)來(lái)。在dispatchMessage中咪惠。優(yōu)先級(jí)有三種击吱。優(yōu)先級(jí)最高的是message自己的callback,然后是Handler的callback遥昧,當(dāng)兩者都沒有的時(shí)候覆醇,就由我們重寫來(lái)處理。這也是我們最常用的一種方法炭臭。

那么Looper是什么時(shí)候啟動(dòng)的呢?

答案在ActivityThread中永脓。

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"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}

其中Looper.prepareMainLooper()和Looper.prepare()等價(jià)。這個(gè)ActivityThread也就是我們所說(shuō)的UI線程鞋仍,Looper在UI線程啟動(dòng)的時(shí)候常摧,就一直在后臺(tái)默默工作了。

思考威创,Handler怎么做到線程切換處理對(duì)象的?

因?yàn)椴煌€程共享內(nèi)存落午,Handler在A線程發(fā)送了一個(gè)消息,然后主線程的Looper在主線程把消息取了出來(lái)肚豺,同時(shí)交給了Handler溃斋,所以Handler一取一存就做到了線程切換。

小結(jié)

其實(shí)看源碼本沒有那么復(fù)雜详炬,有時(shí)候根本不需要抽絲剝繭一條條代碼看盐类,代碼量很多寞奸,難度確實(shí)是有的呛谜。但是我們只要有一個(gè)目標(biāo),有一條主線枪萄,只尋找最關(guān)鍵的代碼隐岛,就能很快的理清它的脈絡(luò)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瓷翻,一起剝皮案震驚了整個(gè)濱河市聚凹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌齐帚,老刑警劉巖妒牙,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異对妄,居然都是意外死亡湘今,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門剪菱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)摩瞎,“玉大人拴签,你說(shuō)我怎么就攤上這事∑烀牵” “怎么了蚓哩?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)上渴。 經(jīng)常有香客問(wèn)我岸梨,道長(zhǎng),這世上最難降的妖魔是什么稠氮? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任盛嘿,我火速辦了婚禮,結(jié)果婚禮上括袒,老公的妹妹穿的比我還像新娘次兆。我一直安慰自己,他們只是感情好锹锰,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布芥炭。 她就那樣靜靜地躺著,像睡著了一般恃慧。 火紅的嫁衣襯著肌膚如雪园蝠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天痢士,我揣著相機(jī)與錄音彪薛,去河邊找鬼。 笑死怠蹂,一個(gè)胖子當(dāng)著我的面吹牛善延,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播城侧,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼易遣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嫌佑?” 一聲冷哼從身側(cè)響起豆茫,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎屋摇,沒想到半個(gè)月后揩魂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炮温,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年火脉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡忘分,死狀恐怖棋枕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情妒峦,我是刑警寧澤重斑,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站肯骇,受9級(jí)特大地震影響窥浪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜笛丙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一漾脂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胚鸯,春花似錦骨稿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至哥桥,卻和暖如春辙浑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拟糕。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工判呕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人送滞。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓侠草,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親累澡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梦抢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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