Android 消息機制

主線程Looper創(chuàng)建的位置

ActivityThread的main方法中屑宠,也就是消息機制開始建立的地方


        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        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();

1. 調(diào)用了Looper.prepareMainLooper()方法军援,內(nèi)部又調(diào)用了prepare()方法

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

可以看到,new了一個looper對象,存到了sThreadLocal中涛菠。

2. 調(diào)用new Looper(quitAllowed)構造方法,創(chuàng)建了MessageQueue消息隊列铺然。

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

3. thread.getHandler()方法,拿到H(),其實也就是拿到Handler對象且轨。

new了ActivityThread對象浮声,此時H()對象也隨著創(chuàng)建出來。

   ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

H()對象其實也就是Handler對象旋奢,調(diào)用了handler的無參構造

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

最終調(diào)用如下代碼泳挥,完成了H()對消息隊列和Looper的引用的持有。

public Handler(@Nullable 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());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

4. 調(diào)用Looper.loop()開啟死循環(huán)至朗,至此Android的消息機制開啟

myLooper()會取出ThreadLocal里存儲的Looper對象屉符,并取出Looper中的消息隊列,然后死循環(huán)锹引,開始從消息隊列中取消息矗钟,通過msg.target(也就是發(fā)送消息的handler對象)的dispatchMessage()方法,把消息交給handler去處理嫌变。

/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    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;
        ...
        for (;;) {
            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);
            }

            try {
                msg.target.dispatchMessage(msg);
        
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          
            ...

            msg.recycleUnchecked();
        }
    }

總結

  • 一個線程中只允許有一個Looper和消息隊列

原因:1.代碼限制真仲;2.多個會導致混亂

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

Looper構造方法內(nèi)創(chuàng)建了一個消息隊列,多了不利于維護初澎。

  • 一個線程中可以有多個Handler對象

  • 線程間通過可以Handler在A,B之間通信是因為Handler在A線程創(chuàng)建時候秸应,持有了A的Looper和消息隊列,B線程持有了A的引用碑宴,所以可以通過Handler發(fā)送消息給A软啼。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市延柠,隨后出現(xiàn)的幾起案子祸挪,更是在濱河造成了極大的恐慌,老刑警劉巖贞间,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贿条,死亡現(xiàn)場離奇詭異雹仿,居然都是意外死亡,警方通過查閱死者的電腦和手機整以,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門胧辽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人公黑,你說我怎么就攤上這事邑商。” “怎么了凡蚜?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵人断,是天一觀的道長。 經(jīng)常有香客問我朝蜘,道長恶迈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任谱醇,我火速辦了婚禮蝉绷,結果婚禮上,老公的妹妹穿的比我還像新娘枣抱。我一直安慰自己熔吗,他們只是感情好,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布佳晶。 她就那樣靜靜地躺著桅狠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪轿秧。 梳的紋絲不亂的頭發(fā)上中跌,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機與錄音菇篡,去河邊找鬼漩符。 笑死,一個胖子當著我的面吹牛驱还,可吹牛的內(nèi)容都是我干的嗜暴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼议蟆,長吁一口氣:“原來是場噩夢啊……” “哼闷沥!你這毒婦竟也來了?” 一聲冷哼從身側響起咐容,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤舆逃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體路狮,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡虫啥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了奄妨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涂籽。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖展蒂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苔咪,我是刑警寧澤锰悼,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站团赏,受9級特大地震影響箕般,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜舔清,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一丝里、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧体谒,春花似錦杯聚、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至故响,卻和暖如春傀广,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彩届。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工伪冰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人樟蠕。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓贮聂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親寨辩。 傳聞我的和親對象是個殘疾皇子寂汇,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360