Handler原理

只需要解釋一點:為什么handler可以用于子線程更新UI
(1)當(dāng)UI線程創(chuàng)建的時候會執(zhí)行ActivityThread的main方法:

Looper.prepareMainLooper()

 public static void prepareMainLooper() {
        
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                //每個線程只允許有一個Looper,所以當(dāng)該線程的Looper存在時,報異常
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

我們看看prepare()方法做了什么

private static void prepare(boolean quitAllowed) {
        //sThreadLocal 是一個ThreadLocal類型的實例,這個類的作用簡單說就是為每個線程提供某個變量的副本來保證線程安全,以后會詳細(xì)說
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

以上兩步做一個總結(jié),UI線程在初始化的時候會實例化一個Looper對象和一個MessageQueue對象,注意:一個線程只有一個looper實例,只有一個MessageQueue實例.
(2)ActivityThread中的另外一個方法:loop()

//不需要每行都看
 public static void loop() {
        //myLooper()返回的就是當(dāng)前線程的looper對象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //獲取當(dāng)前線程的MessageQueue對象
        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();
        //開啟無線循環(huán)去取消息
        for (;;) {
            //這里可能發(fā)生阻塞,為什么會阻塞?因為queue有可能取不出消息,因此就會等待消息
            Message msg = queue.next();
            //當(dāng)取出的消息為空,退出循環(huán).什么時候為空,需要看next源碼,到這里我們看看next()方法做了什么
            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 traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                //這里是關(guān)鍵點,取出消息來以后,把消息交給它的target對象去處理,target是誰,一會揭曉答案
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
         ...

            msg.recycleUnchecked();
        }
    }

我們看看MessageQueue的next()方法做了什么

//只需要看中文注釋的地方
 Message next() {
        //這個mPtr決定了是否返回null,返回null的話則looper的loop()方法就會退出,也就是不會再處理消息
        //used by native code 這個是這個字段的解釋,也就是說這個字段由native方法控制
        //當(dāng)我們調(diào)用looper的quit()或者quitSafely()方法的時候,這個字段的值就會被賦值為0
        //quit()指的是立即退出消息循環(huán),不管MessageQueue里面還有沒有待處理的消息
        //quitSafely()是等MessageQueue里面的消息處理完成后再退出
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        //開啟無限循環(huán)去取消息,不用深究
        for (;;) {
          ...
    }

(3)通過looper的loop()方法,我們知道最終處理消息的是msg的target對象,下面我們來看看這個target是什么
我們在使用handler的時候,會使用這個方法:

handler.sendMessage(msg);

我們看看這個方法做了什么:

public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
接著看:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
接著看:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        //這個消息隊列,就是當(dāng)前線程的唯一消息隊列,就是在創(chuàng)建looper的時候創(chuàng)建的那個唯一的MessageQueue,handler在哪個線程創(chuàng)建,這個MessageQueue就是哪個線程的
        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);
    }
接著看:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        //這個就是關(guān)鍵點!target對象就是handler本身,也就是說這個msg是誰發(fā)出的,最終由誰的dispatchMessage()方法處理!!!
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //這個方法就是把消息加入到MessageQueue的消息隊列里面,本著先進先出的原則,先加入到隊列的消息優(yōu)先處理
        return queue.enqueueMessage(msg, uptimeMillis);
    }

(4)從上面看出方法的最終執(zhí)行者是我們創(chuàng)建的handler的dispatchMessage()方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //當(dāng)使用handler.post(Runnable r)這個方法的時候會執(zhí)行這個方法,這個方法說到底就是執(zhí)行r中的run()方法
            handleCallback(msg);
        } else {
            //看到handleMessage()方法可能會更加熟悉,因為我們在創(chuàng)建handler的時候會重寫這個方法,也就是最終調(diào)用的是我們重寫的方法
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

(5)handler不僅可以用來更新ui,我們還可以在子線程創(chuàng)建handler,然后在其他線程使用這個handl實例發(fā)送消息,在子線程處理相應(yīng)的邏輯,注意我們需要手動Looper.prepare().

new Thread(){
            @Override
            public void run() {
                Looper.prepare();
                 handler = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                    }
                };
                super.run();
            }
        }.start();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末候衍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搁宾,老刑警劉巖隆圆,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沾歪,死亡現(xiàn)場離奇詭異,居然都是意外死亡霹粥,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進店門疼鸟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來后控,“玉大人,你說我怎么就攤上這事空镜∫涫矗” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵姑裂,是天一觀的道長馋袜。 經(jīng)常有香客問我,道長舶斧,這世上最難降的妖魔是什么欣鳖? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任疾嗅,我火速辦了婚禮低淡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘访娶。我一直安慰自己矾缓,他們只是感情好怀酷,可當(dāng)我...
    茶點故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗜闻,像睡著了一般蜕依。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天样眠,我揣著相機與錄音友瘤,去河邊找鬼。 笑死檐束,一個胖子當(dāng)著我的面吹牛辫秧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播被丧,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼盟戏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了甥桂?” 一聲冷哼從身側(cè)響起抓半,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎格嘁,沒想到半個月后笛求,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡糕簿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年探入,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片懂诗。...
    茶點故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜂嗽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殃恒,到底是詐尸還是另有隱情植旧,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布离唐,位于F島的核電站病附,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏亥鬓。R本人自食惡果不足惜完沪,卻給世界環(huán)境...
    茶點故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嵌戈。 院中可真熱鬧覆积,春花似錦、人聲如沸熟呛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽庵朝。三九已至吗冤,卻和暖如春又厉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背欣孤。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留昔逗,地道東北人降传。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像勾怒,于是被迫代替她去往敵國和親婆排。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,472評論 2 348

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