Handler吗跋、Looper、Message宁昭、MessageQueue分析

以下內(nèi)容整理自互聯(lián)網(wǎng)跌宛,僅用于個(gè)人學(xué)習(xí)


Android的線程間通信就靠Handler、Looper积仗、Message疆拘、MessageQueue。

1. Looper

先來(lái)看看looper.prepare()這個(gè)方法寂曹,它的作用是確保每個(gè)線程只有一個(gè)Looper哎迄。

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //在當(dāng)前線程綁定一個(gè)Looper
        sThreadLocal.set(new Looper(quitAllowed));
}

以上代碼只做了兩件事情: 1. 判斷當(dāng)前線程有木有Looper,如果有則拋出異常隆圆。 2. 如果沒(méi)有的話(huà)漱挚,那么就設(shè)置一個(gè)新的Looper到當(dāng)前線程。

上述代碼中調(diào)用的Looper的構(gòu)造函數(shù)渺氧,接下來(lái)看看構(gòu)造函數(shù)

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

Looper在構(gòu)造函數(shù)里干了兩件事情: 1. 將線程對(duì)象指向了創(chuàng)建Looper的線程旨涝。 2. 創(chuàng)建了一個(gè)新的MessageQueue。

再來(lái)看看looper.loop()方法

public static void loop() {
        final Looper me = myLooper();//獲得當(dāng)前線程綁定的Looper
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;//獲得與Looper綁定的MessageQueue

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

        //進(jìn)入死循環(huán)侣背,不斷地去取對(duì)象白华,分發(fā)對(duì)象到Handler中消費(fèi)
        for (;;) {
            Message msg = queue.next(); // 不斷的取下一個(gè)Message對(duì)象,在這里可能會(huì)造成堵塞贩耐。
            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
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            //在這里弧腥,開(kāi)始分發(fā)Message
            msg.target.dispatchMessage(msg);

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

            //當(dāng)分發(fā)完Message之后,當(dāng)然要標(biāo)記將該Message標(biāo)記為 *正在使用* 啦
            msg.recycleUnchecked();
        }
    }

分析了上面的源代碼潮太,我們可以意識(shí)到管搪,最重要的方法是:

  1. queue.next()
  2. msg.target.dispatchMessage(msg)
  3. msg.recycleUnchecked()

其實(shí)Looper中最重要的部分都是由Message、MessageQueue組成的消别!這段最重要的代碼中涉及到了四個(gè)對(duì)象,他們與彼此的關(guān)系如下:

  1. MessageQueue:裝食物的容器
  2. Message:被裝的食物
  3. Handler(msg.target實(shí)際上就是Handler):食物的消費(fèi)者
  4. Looper:負(fù)責(zé)分發(fā)食物的人

looper函數(shù)就是不斷從消息隊(duì)列里面取消息抛蚤,如果隊(duì)列里面沒(méi)有消息,就阻塞寻狂,直到有消息岁经,則把這個(gè)消息取出,并調(diào)用Hander的dispatchMessage函數(shù)蛇券。

2. Handler

先來(lái)分析Handler的構(gòu)造函數(shù)

public Handler() {   
        this(null, false);   
}   
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());   
        }   
    }   
    //獲取與創(chuàng)建Handler線程綁定的Looper 
    mLooper = Looper.myLooper();   
    if (mLooper == null) {   
        throw new RuntimeException(   
            "Can't create handler inside thread that has not called Looper.prepare()");   
    }   
    //獲取與Looper綁定的MessageQueue
    //因?yàn)橐粋€(gè)Looper就只有一個(gè)MessageQueue缀壤,也就是與當(dāng)前線程綁定的MessageQueue
    mQueue = mLooper.mQueue;   
    mCallback = callback;   
    mAsynchronous = async;   
}

Handler通過(guò)獲取當(dāng)前線程的Looper對(duì)象樊拓,通過(guò)Looper對(duì)象來(lái)獲取并保存了消息隊(duì)列。Handler為什么要獲取消息隊(duì)列的引用呢塘慕?主要是筋夏,Handler需要把消息“塞”進(jìn)消息隊(duì)列里面,因此必須得有消息隊(duì)列引用图呢。

發(fā)送消息的sendMessage函數(shù)

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) {   
    //引用Handler中的MessageQueue
    //這個(gè)MessageQueue就是創(chuàng)建Looper時(shí)被創(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;   
    }   
    //將新來(lái)的Message加入到MessageQueue中
    return enqueueMessage(queue, msg, uptimeMillis);   
}   
 
 
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {   
   //賦值
   msg.target = this;   
   if (mAsynchronous) {   
       msg.setAsynchronous(true);   
   }   
   return queue.enqueueMessage(msg, uptimeMillis);   
}

從上面代碼看出条篷,sendMessage其本質(zhì)就是對(duì)應(yīng)一個(gè)消息入隊(duì)的過(guò)程。

前面我們知道蛤织,Looper從隊(duì)列里面取出消息后赴叹,調(diào)用Handler的dispatchMessage函數(shù)

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

可以看到,其實(shí)就是調(diào)用了handleMessage函數(shù)指蚜,而我們平時(shí)定義Handler對(duì)象時(shí)就是重寫(xiě)這個(gè)函數(shù)乞巧,因此取出消息后,會(huì)調(diào)用我們定義的handleMessage摊鸡。

總結(jié)

當(dāng)我們調(diào)用handler.sendMessage(msg)方法發(fā)送一個(gè)Message時(shí)绽媒,實(shí)際上這個(gè)Message是發(fā)送到與當(dāng)前線程綁定的一個(gè)MessageQueue中,然后與當(dāng)前線程綁定的Looper將會(huì)不斷的從MessageQueue中取出新的Message免猾,調(diào)用msg.target.dispathMessage(msg)方法將消息分發(fā)到與Message綁定的handler.handleMessage()方法中是辕。

一個(gè)Thread對(duì)應(yīng)多個(gè)Handler,一個(gè)Thread對(duì)應(yīng)一個(gè)Looper和MessageQueue掸刊,Handler與Thread共享Looper和MessageQueue免糕。 Message只是消息的載體,將會(huì)被發(fā)送到與線程綁定的唯一的MessageQueue中忧侧,并且被與線程綁定的唯一的Looper分發(fā)石窑,被與其自身綁定的Handler消費(fèi)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蚓炬,一起剝皮案震驚了整個(gè)濱河市松逊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肯夏,老刑警劉巖经宏,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異驯击,居然都是意外死亡烁兰,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)徊都,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)沪斟,“玉大人,你說(shuō)我怎么就攤上這事暇矫≈髦” “怎么了择吊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)槽奕。 經(jīng)常有香客問(wèn)我几睛,道長(zhǎng),這世上最難降的妖魔是什么粤攒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任所森,我火速辦了婚禮,結(jié)果婚禮上夯接,老公的妹妹穿的比我還像新娘必峰。我一直安慰自己,他們只是感情好钻蹬,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著凭需,像睡著了一般问欠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粒蜈,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天顺献,我揣著相機(jī)與錄音,去河邊找鬼枯怖。 笑死注整,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的度硝。 我是一名探鬼主播肿轨,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蕊程!你這毒婦竟也來(lái)了椒袍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤藻茂,失蹤者是張志新(化名)和其女友劉穎驹暑,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體辨赐,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡优俘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了掀序。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帆焕。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖森枪,靈堂內(nèi)的尸體忽然破棺而出视搏,到底是詐尸還是另有隱情审孽,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布浑娜,位于F島的核電站佑力,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏筋遭。R本人自食惡果不足惜打颤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漓滔。 院中可真熱鬧编饺,春花似錦、人聲如沸响驴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)豁鲤。三九已至秽誊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳骡,已是汗流浹背锅论。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留楣号,地道東北人最易。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像炫狱,于是被迫代替她去往敵國(guó)和親藻懒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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