Android的消息機(jī)制(java層)

前言

Android的消息機(jī)制用于同進(jìn)程的線程間通信瓣赂,它是由MessageQueue嗓化,Message撩鹿,Looper睡扬,Handler共同組成盟蚣,Android中大量的交互都是通過消息機(jī)制,比如四大組件啟動(dòng)過程與服務(wù)的交互卖怜、View的繪制屎开、更新等都離不開消息機(jī)制,所以Android在某種意義上也可以說成是一個(gè)以消息驅(qū)動(dòng)的系統(tǒng)马靠,在Android中消息機(jī)制的運(yùn)作分為java層和native層奄抽,它們之間的運(yùn)作機(jī)制不一樣,本文講解的是java層的消息機(jī)制甩鳄,如果你想了解native層的消息機(jī)制逞度,可以閱讀:

Android消息機(jī)制(native層)

但其實(shí)java層的消息機(jī)制的核心功能都交給了native層的消息機(jī)制來完成,但作為應(yīng)用開發(fā)妙啃,首先要掌握java層的消息機(jī)制档泽。

本文源碼基于Android8.0烦粒,源碼相關(guān)位置:
frameworks/base/core/java/android/os/*.java (*代表MessageQueue谬擦、Handler荣堰、Looper酱塔、Message)

消息機(jī)制概述

Android應(yīng)用的每個(gè)事件都會(huì)轉(zhuǎn)化為一個(gè)系統(tǒng)消息即Message杉女,消息中包含了事件的相關(guān)信息和消息的處理人即Handler改鲫,消息要通過Handler發(fā)送仑撞,最終被投遞到一個(gè)消息隊(duì)列中即MessageQueue聋袋,它維護(hù)了一個(gè)待處理的消息列表突倍,然后通過Looper開啟了一個(gè)消息循環(huán)不斷地從這個(gè)隊(duì)列中取出消息腔稀,當(dāng)從消息隊(duì)列取出一個(gè)消息后,Looper根據(jù)消息的處理人(target)將此消息分發(fā)給相應(yīng)的Handle處理羽历,整個(gè)過程如下圖所示焊虏。

它們的工作原理就像工廠的生產(chǎn)線,Looper是發(fā)動(dòng)機(jī)秕磷,MessageQueue是傳送帶诵闭,Handler是工人,Message則是待處理的產(chǎn)品。

java層消息機(jī)制架構(gòu)圖

handler2.png
  • Looper --- 是每個(gè)線程的MessageQueue管家疏尿,里面有一個(gè)MessageQueue消息隊(duì)列瘟芝,負(fù)責(zé)把消息從MessageQueue中取出并把消息傳遞到Handler中去,每個(gè)線程只有一個(gè)Looper褥琐;
  • MessageQueue --- 消息隊(duì)列锌俱,有一組待處理的Message,主要用于存放所有通過Handler發(fā)送的消息敌呈,每個(gè)線程只有一個(gè)MessageQueue贸宏;
  • Message --- 是線程之間傳遞的消息,里面有一個(gè)用于處理消息的Handler磕洪;
  • Handler --- 主要用于發(fā)送和處理消息吭练,里面有Looper和MessageQueue。

簡(jiǎn)單使用

在開發(fā)中析显,我們?cè)谧泳€程中執(zhí)行完操作后通常需要更新UI鲫咽,但我們都知道不能在子線程中更新UI,此時(shí)我們就要通過Handler將一個(gè)消息post到UI線程中谷异,然后再在Handler中的handleMessage方法中進(jìn)行處理分尸,如果我們不傳遞UI線程所屬的Looper去創(chuàng)建Handler,那么該Handler必須在主線程中創(chuàng)建晰绎,如下:

//在主線程中創(chuàng)建Handler 
Handler mHandler = new Handler(){
 @Override
 public void handleMessage(Message msg){
    //更新UI
 }
}

//在子線程中進(jìn)行耗時(shí)操作
new Thread(){
    public void run(){
        mHandler.sendEmptyMessage(0);
    }
}

以上就是我們平時(shí)使用Handler的常規(guī)用法了寓落。

接下來我們以應(yīng)用主線程(又叫做UI線程)的消息機(jī)制運(yùn)作為例講解Android消息機(jī)制的原理括丁。

源碼分析

1荞下、 Looper的創(chuàng)建

我們知道Android應(yīng)用程序的入口實(shí)際上是ActivityThread.main方法,而應(yīng)用的消息循環(huán)也是在這個(gè)方法中創(chuàng)建史飞,具體源碼如下:

//ActivityThread.java 
public static void main(String[] args) {
     //...
     //1尖昏、創(chuàng)建消息循環(huán)Looper
     Looper.prepareMainLooper();
     //...
     //2、執(zhí)行消息循環(huán)
     Looper.loop();
 }

我們關(guān)注注釋1构资,ActivityThread調(diào)用了Looper的prepareMainLooper方法來創(chuàng)建Looper實(shí)例抽诉,Looper的prepareMainLooper方法如下:

//Looper.java 
public static void prepareMainLooper() {
    //1、創(chuàng)建不允許退出的Looper
    prepare(false);
    synchronized (Looper.class) {//保證只有一個(gè)線程執(zhí)行
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already            been        prepared.");
        }
        //2吐绵、將剛剛創(chuàng)建的Looper實(shí)例賦值給sMainLooper
        //sMainLooper字段是Looper類中專門為UI線程保留的迹淌,只要某個(gè)線程調(diào)用了prepareMainLooper方法,把它創(chuàng)建的Looper實(shí)例賦值給sMainLooper己单,它就可以成為UI線程唉窃,prepareMainLooper方法只允許執(zhí)行一次
        sMainLooper = myLooper();
    }
}

private static void prepare(boolean quitAllowed){//quitAllowed表示是否允許Looper運(yùn)行時(shí)退出
    //Looper.prepare()只能執(zhí)行一次
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread"); 
     }
    //把Looper保存到TLS中
    sThreadLocal.set(new Looper(quitAllowed));
}

public static @Nullable Looper myLooper() {
    //獲取當(dāng)前線程TLS區(qū)域的Looper
    return sThreadLocal.get();
}

private static Looper sMainLooper;  // guarded by Looper.class
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

線程在調(diào)用prepareMainLooper方法后,就代表它成為了一個(gè)UI線程纹笼,可以看到prepareMainLooper方法中是調(diào)用Looper的prepare方法來創(chuàng)建Looper實(shí)例纹份,當(dāng)要獲取創(chuàng)建的Looper實(shí)例時(shí),是通過Looper的myLooper方法來獲取的,在調(diào)用prepare方法時(shí)蔓涧,會(huì)把創(chuàng)建的Looper實(shí)例set到ThreadLocal中件已,當(dāng)獲取時(shí)也會(huì)從ThreadLocal中通過get方法獲取,那么ThreadLocal是什么元暴?這里簡(jiǎn)單介紹一下ThreadLocal:

ThreadLocal:線程本地存儲(chǔ)區(qū)(Thread Local Storage篷扩,簡(jiǎn)稱為TLS),每 個(gè)線程都有自己的私有的本地存儲(chǔ)區(qū)域茉盏,不同線程之間彼此不能訪問對(duì)方的TLS區(qū)域瞻惋。

它的常用操作方法有
ThreadLocal.set(T value):將value存儲(chǔ)到當(dāng)前線程的TLS區(qū)域。
ThreadLocal.get():獲取當(dāng)前線程TLS區(qū)域的數(shù)據(jù)

關(guān)于ThreadLocal更多信息可以查看ThreadLocal原理解析

ThreadLocal的get()和set()方法操作的類型都是泛型援岩,我們從ThreadLocal在Looper中的定義可以看出歼狼,sThreadLocal的get()和set()操作的類型都是Looper類型, 所以,由于ThreadLocal的作用享怀,每個(gè)線程只能保存一個(gè)Looper羽峰,不同線程的Looper是不相同的,這樣添瓷,通過調(diào)用Looper.prepare(false)方法梅屉,UI線程中就保存了它對(duì)應(yīng)的鳞贷、唯一的Looper實(shí)例坯汤,當(dāng)在UI線程調(diào)用Looper.myLooper方法時(shí)它就會(huì)返回UI線程關(guān)聯(lián)的Looper實(shí)例,當(dāng)然搀愧,如果你在子線程中想要獲得UI線程關(guān)聯(lián)的Looper實(shí)例惰聂,就需要調(diào)用getMainLooper方法,該方法如下:

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        //返回UI線程的Looper實(shí)例
        return sMainLooper;
    }
}

以上就是UI線程的Looper的創(chuàng)建過程咱筛,執(zhí)行ActivityThread.main后搓幌,應(yīng)用程序就啟動(dòng)了,UI的消息循環(huán)也在Looper.loop方法中啟動(dòng)迅箩,此后Looper會(huì)一直從消息隊(duì)列中取出消息溉愁,用戶或系統(tǒng)通過Handler不斷往消息隊(duì)列中添加消息,這些消息不斷的被取出饲趋,處理拐揭,回收,使得應(yīng)用運(yùn)轉(zhuǎn)起來奕塑。

我們?cè)谄綍r(shí)開發(fā)時(shí)堂污,一般是使用不帶參數(shù)的prepare()方法來創(chuàng)建子線程對(duì)應(yīng)的Looper實(shí)例,如下:

//我們平時(shí)使用的prepare方法
public static void prepare() {
    //quitAllowed = true
    prepare(true);
}

和UI線程的區(qū)別是爵川,UI線程的Looper是不允許推出的敷鸦,而我們的Looper一般是允許退出的。

2、MessageQueue的創(chuàng)建

我們?cè)谏厦嬷腊桥{(diào)用Looper的prepare方法就會(huì)創(chuàng)建Looper實(shí)例值依,同時(shí)會(huì)把Looper實(shí)例通過ThreadLocal保存到線程中,在創(chuàng)建Looper時(shí)還會(huì)同時(shí)在構(gòu)造中創(chuàng)建MessageQueue實(shí)例碟案,如下:

//Looper.java
final MessageQueue mQueue;
//Looper唯一的構(gòu)造函數(shù)
private Looper(boolean quitAllowed) {
    //可以看到MessageQueue是在Looper中創(chuàng)建的
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

Looper只有這一個(gè)構(gòu)造函數(shù)愿险,并且它是私有的,所以我們只能通過Looper的prepare方法創(chuàng)建Looper實(shí)例价说,由于ThreadLocal辆亏,每個(gè)線程最多只能對(duì)應(yīng)一個(gè)Looper實(shí)例,而每個(gè)Looper內(nèi)部只有一個(gè)MessageQueue實(shí)例鳖目,推出:每個(gè)線程最多對(duì)應(yīng)一個(gè)MessageQueue實(shí)例扮叨。

我們看一下MessageQueue的構(gòu)造,如下:

//MessageQueue.java
private long mPtr; // used by native code
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //通過native方法初始化消息隊(duì)列领迈,其中mPtr是供native代碼使用
    mPtr = nativeInit();
}

MessageQueue的構(gòu)造中會(huì)通過native方法在native層創(chuàng)建一個(gè)屬于native層的消息隊(duì)列NativeMessageQueue彻磁,然后把NativeMessageQueue的地址返回給java層保存在mPtr中,java層和native層之間的通信就通過這個(gè)mPtr指針狸捅。

MessageQueue是消息機(jī)制的核心類衷蜓,它是java層和native層的連接紐帶,它里面有大量的native方法尘喝,Android有倆套消息機(jī)制(java層和native層磁浇,實(shí)現(xiàn)不一樣),但本文只講解java層的消息機(jī)制朽褪,不會(huì)涉及到native層.

關(guān)于native層的查看Android消息機(jī)制(native層)

我們通過Looper的myQueue方法就能獲取到它關(guān)聯(lián)的MessageQueue實(shí)例置吓,如下:

//Looper.java
public static @NonNull MessageQueue myQueue() {
    //先通過TLS獲取Looper實(shí)例,再從對(duì)應(yīng)的Looper中獲取MessageQueue實(shí)例
    return myLooper().mQueue;
}

3鞍匾、消息循環(huán)的運(yùn)行

在ActivityThread的mian方法在創(chuàng)建Looper后交洗,通過Looper.loop方法就啟動(dòng)了消息循環(huán),這個(gè)函數(shù)會(huì)不斷的從MessageQueue中取出消息橡淑、處理消息,我們點(diǎn)進(jìn)此方法看一下它的源碼:

//Looper.java
public static void loop() {   
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    //從Looper中取出MessageQueue
    final MessageQueue queue = me.mQueue;
    //...
    for (;;) {
        //1咆爽、從MessageQueue中取出消息梁棠,沒有消息時(shí)會(huì)阻塞等待
        Message msg = queue.next(); 
        //next()返回了null,表示MessageQueue正在退出斗埂,即調(diào)用了Looper的quit或quitSafely方法
        if (msg == null) {
            return;
        }
        //...
        //2符糊、分發(fā)消息
        msg.target.dispatchMessage(msg);
        //...
        //3、回收消息
        msg.recycleUnchecked();
    }
}

loop()中是一個(gè)死循環(huán)呛凶,我們關(guān)注注釋1男娄,loop()會(huì)調(diào)用MessageQueue的next()來獲取最新的消息,當(dāng)沒有消息時(shí),next()會(huì)一直阻塞在那里模闲,這也導(dǎo)致loop()阻塞建瘫,唯一跳出循環(huán)的條件是next()返回null,這時(shí)代表Looper的quit()或quitSafely()被調(diào)用尸折,從而調(diào)用MessageQueue的quit()來通知消息隊(duì)列退出啰脚,如下:

//Looper.java
public void quit() {
    mQueue.quit(false);
}

//quitSafely和quit方法的區(qū)別是,quitSafely方法會(huì)等MessageQueue中所有的消息處理完后才退出实夹,而quit會(huì)直接退出
public void quitSafely() {
    mQueue.quit(true);
}

所以MessageQueue的next()是最關(guān)鍵的函數(shù)橄浓,我們來看看next函數(shù)的關(guān)鍵代碼:

//MessageQueue.java
Message next() {
    //mPtr是在構(gòu)造中被賦值,是指向native層的MessageQueue
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; 
    int nextPollTimeoutMillis = 0;
    //一個(gè)死循環(huán)亮航,里面分為兩部分荸实,1、處理java層消息缴淋;2泪勒、如果沒有消息處理,執(zhí)行IdleHandler
    for(;;){
        //...
        
        //Part1:獲取java層的消息處理
        
        //nativePollOnce方法用于處理native層消息宴猾,是一個(gè)阻塞操作
        //它在這兩種情況下返回:1圆存、等待nextPollTimeoutMillis時(shí)長(zhǎng)后;2仇哆、MessageQueue被喚醒
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            //mMessages是java層的消息隊(duì)列沦辙,這里表示取出消息隊(duì)列的第一個(gè)消息
            Message msg = mMessages;
            if (msg != null && msg.target == null) {//遇到同步屏障(target為null的消息)
                //在do-while中找到異步消息,優(yōu)先處理異步消息
                //異步消息的isAsynchronous方法返回true
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {//有消息
                if (now < msg.when) { //消息還沒到觸發(fā)時(shí)間
                    //設(shè)置下一次輪詢的超時(shí)時(shí)長(zhǎng)(等待時(shí)長(zhǎng))
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {//now == msg.when, 消息到達(dá)觸發(fā)時(shí)間
                    mBlocked = false;
                    //從mMessages的頭部獲取一條消息并返回 
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //設(shè)置消息的使用狀態(tài)讹剔,即flags |= FLAG_IN_US
                    msg.markInUse();
                    //返回這個(gè)消息
                    return msg;
                }
            } else {//msg == null油讯,沒有消息
                //設(shè)置nextPollTimeoutMillis為-1,準(zhǔn)備進(jìn)入阻塞延欠,等待MessageQueue被喚醒
                nextPollTimeoutMillis = -1;
            }
            
            //調(diào)用了quit方法
            if (mQuitting) {
                dispose();
                return null;
            }
            
            //當(dāng)處于以下2種情況時(shí)陌兑,就會(huì)執(zhí)行到Part2:
            //1、mMessages == null由捎,java層沒有消息處理
            //2兔综、now < msg.when,有消息處理狞玛,但是還沒有到消息的執(zhí)行時(shí)間
            //1软驰、2兩種情況都表明線程的消息隊(duì)列處于空閑狀態(tài),處于空閑狀態(tài)心肪,就會(huì)執(zhí)行IdleHandler
            
           //Part2:沒有消息處理锭亏,執(zhí)行IdleHandler
           //mIdleHandlers表示IdleHandler列表
           //pendingIdleHandlerCount表示需要執(zhí)行的IdleHandler的數(shù)量
 
            if (pendingIdleHandlerCount < 0
                && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //沒有IdleHandler需要處理,可直接進(jìn)入阻塞
                mBlocked = true;
                continue;
            }
           //有IdleHandler需要處理
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            //把mIdleHandlers列表轉(zhuǎn)成mPendingIdleHandlers數(shù)組
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        //遍歷mPendingIdleHandlers數(shù)組
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            //取出IdleHandler
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
                //執(zhí)行IdleHandler的queueIdle方法硬鞍,通過返回值由自己決定是否保持存活狀態(tài)
                keep = idler.queueIdle();
            }
            //...省略異常處理
            
            if (!keep) {
                // 不需要存活慧瘤,移除
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
        
        //重置pendingIdleHandlerCount和nextPollTimeoutMillis為0
        pendingIdleHandlerCount = 0;
        //nextPollTimeoutMillis為0戴已,表示下一次循環(huán)會(huì)馬上從Messages中取出一個(gè)Message判斷是否到達(dá)執(zhí)行時(shí)間
        //因?yàn)镮dleHandler在處理事件的時(shí)間里,有可能有新的消息發(fā)送來過來锅减,需要重新檢查
        nextPollTimeoutMillis = 0;
    }
}

MessageQueue的next方法有點(diǎn)長(zhǎng)糖儡,但是它里面的邏輯是很好理解的,它主要是通過一個(gè)死循環(huán)不斷的返回Message給Looper處理上煤,next方法可以分為兩部分閱讀:

1休玩、獲取java層的消息處理

我們看Part1,先執(zhí)行nativePollOnce方法劫狠,它是一個(gè)阻塞操作拴疤,其中nextPollTimeoutMillis代表下一次等待的超時(shí)時(shí)長(zhǎng),當(dāng)nextPollTimeoutMillis = 0時(shí)或到達(dá)nextPollTimeoutMillis時(shí)独泞,它會(huì)立即返回呐矾;當(dāng)nextPollTimeoutMillis = -1時(shí),表示MessageQueue中沒有消息懦砂,會(huì)一直等待下去蜒犯,直到Hanlder往消息隊(duì)列投遞消息,執(zhí)行nativeWake方法后荞膘,MessageQueue被喚醒罚随,nativePollOnce就會(huì)返回,但它此時(shí)并不是立即返回羽资,它會(huì)先處理完native層的消息后淘菩,再返回,然后獲取java層的消息處理屠升;

接著next方法就會(huì)從mMessages鏈表的表頭中獲取一個(gè)消息潮改,首先判斷它是否是同步屏障,同步屏障就是target為null的Message腹暖,如果遇到同步屏障汇在,MessageQueue就會(huì)優(yōu)先獲取異步消息處理,異步消息就是優(yōu)先級(jí)比同步消息高的消息脏答,我們平時(shí)發(fā)送的就是同步消息糕殉,通過Message的setAsynchronous(true)可以把同步消息變成異步消息,不管是同步還是異步以蕴,都是Message糙麦,獲取到Message后;

接著判斷Message是否到達(dá)它的執(zhí)行時(shí)間(if(now == msg.when))丛肮,如果到達(dá)了執(zhí)行時(shí)間,next方法就會(huì)返回這條消息給Looper處理魄缚,并將其從單鏈表中刪除宝与;如果還沒有到達(dá)執(zhí)行時(shí)間焚廊,就設(shè)置nextPollTimeoutMillis為下一次等待超時(shí)時(shí)長(zhǎng),等待下次再次取出判斷习劫,可以發(fā)現(xiàn)雖然MessageQueue叫消息隊(duì)列咆瘟,但它卻不是用隊(duì)列實(shí)現(xiàn)的,而是用鏈表實(shí)現(xiàn)的诽里。

通過MessageQueue的postSyncBarrier方法可以添加一個(gè)同步屏障袒餐,通過removeSyncBarrier方法可以移除相應(yīng)的同步屏障,在Android谤狡,Choreographer機(jī)制中就使用到了異步消息灸眼,在View樹繪制之前,會(huì)先往UI線程的MessageQueue添加一個(gè)同步屏障墓懂,攔截同步消息焰宣,然后發(fā)送一個(gè)異步消息,等待VSYN信號(hào)到來捕仔,觸發(fā)View樹繪制匕积,這樣就可以讓繪制任務(wù)優(yōu)先執(zhí)行。

2榜跌、沒有消息處理闪唆,遍歷IdleHandler列表,執(zhí)行IdleHandler的queueIdle方法

IdleHandler是什么钓葫?IdleHandler是一個(gè)接口悄蕾,它里面只有一個(gè)queueIdle方法,Idle是空閑的意思瓤逼,在MessageQueue空閑的時(shí)候會(huì)執(zhí)行IdleHandler的queueIdle方法笼吟,我們可以通過MessageQueue的addIdleHandler方法添加我們自定義的IdleHandler到mIdleHandlers列表中,如下:

//MessageQueue.java
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); 
public void addIdleHandler(@NonNull IdleHandler handler) {
    //...
    mIdleHandlers.add(handler);
}

MessageQueue空閑霸旗,就代表它處于以下兩種情況:

(1) mMessages == null贷帮,消息隊(duì)列中沒有消息處理;

(2) now < msg.when诱告,有消息處理撵枢,但是還沒有到消息的執(zhí)行時(shí)間.

以上兩種情況之一都會(huì)觸發(fā)next方法遍歷IdleHandler列表,執(zhí)行IdleHandler的queueIdle方法的操作精居。

在Android中锄禽,我們平時(shí)所說的線程空閑其實(shí)就是指線程的MessageQueue空閑,這時(shí)就可以執(zhí)行我們添加的IdleHandler靴姿,例如在LeakCanary中沃但,它通過添加IdleHandler,在UI線程空閑時(shí)執(zhí)行內(nèi)存泄漏的判斷邏輯.

4佛吓、消息的發(fā)送

到這里UI線程已經(jīng)啟動(dòng)了消息循環(huán)宵晚,那么消息從何而來垂攘?消息是由系統(tǒng)產(chǎn)生,然后通過Hanlder發(fā)送到MessageQueue中淤刃,Handler就是用來處理和發(fā)送消息的晒他,應(yīng)用程序的Handler在ActivityThread中被創(chuàng)建,如下:

//ActivityThread.java 
final H mH = new H();

//H定義如下逸贾,繼承Hanldler
//里面定義了大量的字段陨仅,跟Activity的啟動(dòng),Application的綁定等有關(guān)
class H extends Handler {
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int CREATE_SERVICE          = 114;
    //....
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BIND_APPLICATION:
                //...
            case EXIT_APPLICATION:
                //...
            case CREATE_SERVICE:
                //...
                //...
        }
        //...   
}

mH就是應(yīng)用程序內(nèi)部使用的Handler铝侵,我們外部是不可使用的灼伤,應(yīng)用程序通過Handler往MessageQueue中投遞消息,并通過Handler的handlerMessage方法處理消息哟沫,而我們?cè)谕獠恳部梢允褂米约簞?chuàng)建的Handler往UI線程的MessageQueue投遞消息饺蔑。

既然Handle可以往MessageQueue中投遞消息,這說明Handler要和相應(yīng)的MessageQueue關(guān)聯(lián)嗜诀,我們看Handler的構(gòu)造函數(shù)猾警,Handler有兩種構(gòu)造函數(shù):

一種是指定Callback的構(gòu)造函數(shù),Callback默認(rèn)為null隆敢,如下:

//Handler.java
public Handler() {
    this(null, false);
}

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

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

public Handler(Callback callback, boolean async) {
    //...
    //先通過TLS獲取Looper實(shí)例发皿,與Looper關(guān)聯(lián)
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
            + " that has not called Looper.prepare()");
    }
    //通過Looper獲取MessageQueue,與MessageQueue關(guān)聯(lián)
    mQueue = mLooper.mQueue;
    //Callback的作用在消息的分發(fā)中會(huì)講到
    mCallback = callback;
    mAsynchronous = async;
}

在構(gòu)造中如果通過Looper.myLooper方法獲取不到Looper拂蝎,就會(huì)拋出“Can't create handler inside threadxx that has not called Looper.prepare()”異常穴墅,所以如果我們?cè)谧泳€程中使用Handler的默認(rèn)構(gòu)造,沒有先調(diào)用Looper.prepare方法就創(chuàng)建Handler的話温自,就會(huì)拋出上述異常玄货,但是在UI線程中就不會(huì),因?yàn)閼?yīng)用程序啟動(dòng)時(shí)就已經(jīng)調(diào)用了Looper的prepareMainLooper方法悼泌,在該方法里面已經(jīng)調(diào)用了prepare(false)方法創(chuàng)建了UI線程的Looper實(shí)例松捉,無需我們?cè)俅握{(diào)用Looper.prepare方法。

另外一種是指定Looper的構(gòu)造函數(shù)(如果不指定馆里,Looper默認(rèn)從當(dāng)前線程的TLS區(qū)域獲取)隘世,如下:

//Handler.java
public Handler(Looper looper) {
    this(looper, null, false);
}

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

public Handler(Looper looper, Callback callback, boolean async) {
    //與Looper關(guān)聯(lián)
    mLooper = looper;
    //通過Looper獲取MessageQueue,與MessageQueue關(guān)聯(lián)
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

不管哪一種構(gòu)造函數(shù)鸠踪,可以看到丙者,在Handler的最終構(gòu)造中都會(huì)和對(duì)應(yīng)線程的Looper、MessageQueue關(guān)聯(lián)营密,所以就算Hander在子線程創(chuàng)建械媒,我們也可以通過: Handler = new Handler(Looper.getMainLooper);把Handler關(guān)聯(lián)上UI線程的Looper评汰,并通過Looper關(guān)聯(lián)上UI線程的MessageQueue滥沫,這樣侣集,就能把Handler運(yùn)行在UI線程中键俱。

消息的發(fā)送可以通過handler的一系列post方法和一系列的send方法兰绣,一系列post方法最終通過一系列send方法來實(shí)現(xiàn),一系列send方法最終通過enqueueMessage方法來發(fā)送消息编振,如下:

handler3.png

可以發(fā)現(xiàn)Handler所有發(fā)送消息的方法缀辩,最終都是調(diào)用Handler的enqueueMessag方法,該方法源碼如下:

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

在該方法中踪央,首先把Message的target字段設(shè)置為當(dāng)前發(fā)送消息的Handler, 然后設(shè)置Message是否是異步消息臀玄,最后把所有邏輯交給MessageQueue的enqueueMessage方法,該方法的相應(yīng)源碼如下:

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    //...
    synchronized (this) {
        //正在退出時(shí)畅蹂,回收msg健无,加入到消息池
        if (mQuitting) {
            //...
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        boolean needWake;
        //取mMessages鏈表頭部的消息
        Message p = mMessages;
        //滿足以下2種情況之一就把msg插入到鏈表頭部:
        //1、如果p為null液斜,則代表mMessages沒有消息
        //2累贤、如果when == 0 或 when < p.when, 則代表msg的觸發(fā)時(shí)間是鏈表中最早的
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;//如果處于阻塞狀態(tài),需要喚醒
        } else {//3少漆、如果p != null且msg并不是最早觸發(fā)的臼膏,就在鏈表中找一個(gè)位置把msg插進(jìn)去
            //如果處于阻塞狀態(tài),并且鏈表頭部是一個(gè)同步屏障示损,并且插入消息是最早的異步消息渗磅,需要喚醒
            needWake = mBlocked && p.target == null && msg.isAsynchronous();            
            Message prev;
            //下面是一個(gè)鏈表的插入操作, 將消息按時(shí)間順序插入到mMessages中
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }  
                //如果在找到插入位置之前检访,發(fā)現(xiàn)了異步消息的存在始鱼,不需要喚醒
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p;
            prev.next = msg;
        }   
        
        //nativeWake方法會(huì)喚醒當(dāng)前線程的MessageQueue
        if (needWake) {
            nativeWake(mPtr);
        }     
    }
    return true;
}

MessageQueue的enqueueMessage方法主要是一個(gè)鏈表的插入操作,返回true就代表插入成功脆贵,返回false就代表插入失敗医清,它主要分為以下3種情況:

1、插入鏈表頭部

mMessages是按照Message觸發(fā)時(shí)間的先后順序排列的丹禀,越早觸發(fā)的排得越前状勤,頭部的消息是將要最早觸發(fā)的消息,當(dāng)有消息需要加入mMessages時(shí)双泪,如果mMessages為空或這個(gè)消息是最早觸發(fā)的持搜,就會(huì)直接插入鏈表頭部;

2焙矛、插入鏈表的中間位置

如果消息鏈表不為空并且插入的消息不是最早觸發(fā)的葫盼,就會(huì)從鏈表頭部開始遍歷,直到找到消息應(yīng)該插入的合適位置村斟,以保證所有消息的時(shí)間順序贫导,這一個(gè)過程可以理解為插入排序抛猫;

3、判斷是否調(diào)用nativeWake方法

最后孩灯,根據(jù)needWake是否為true闺金,來決定是否調(diào)用nativeWake方法喚醒當(dāng)前線程的MessageQueue,needWake默認(rèn)為false峰档,即不需要喚醒败匹,needWake為true就代表此時(shí)處于以下2種情況:

(1)如果插入消息在鏈表頭部并且mBlocked == true,表示此時(shí)nativePollOnce方法進(jìn)入阻塞狀態(tài)讥巡,等待被喚醒返回掀亩;

(2)如果插入消息在鏈表中間,消息鏈表的頭部是一個(gè)同步屏障欢顷,同時(shí)插入的消息是鏈表中最早的異步消息槽棍,需要喚醒,即時(shí)處理異步消息抬驴。

5炼七、消息的分發(fā)

在消息循環(huán)的運(yùn)行中,如果loop方法中MessageQueue的next方法返回了Message怎爵,那么就會(huì)執(zhí)行到這一句:msg.target.dispatchMessage(msg)特石;Looper會(huì)把這條消息交給該Message的target(Handler對(duì)象)來處理, 實(shí)際上是轉(zhuǎn)了一圈,Handler把消息發(fā)送給MessageQueue鳖链,Looper又把這個(gè)消息給Handler處理姆蘸,下面來看消息分發(fā)邏輯,dispatchMessage()源碼如下:

//Handler.java
public void dispatchMessage(Message msg) {
    //1芙委、檢查msg的callback是否為空
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //2逞敷、Handler的mCallback是否為空
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //3、我們平常處理消息的方法灌侣,該方法默認(rèn)為空推捐,Handler子類通過覆寫該方法來完成具體的邏輯。
        handleMessage(msg);
    }
}

里面代碼量很少侧啼,分為3步:

1牛柒、檢查msg.callback是否為空,不為空則執(zhí)行" handleCallback(msg)", 源碼如下:

//Handler.java
private static void handleCallback(Message message) {
    message.callback.run();
}

msg.callback其實(shí)是一個(gè)Runnable對(duì)象痊乾,當(dāng)我們通過Handler來post一個(gè)Runnable消息時(shí)皮壁,它就不為空,如下:

//Handler.java
public final boolean post(Runnable r){
    return  sendMessageDelayed(getPostMessage(r), 0);
}
//把Runnable對(duì)象包裝成Message對(duì)象
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

可以看到哪审,在post(Runnable r)中蛾魄,會(huì)把Runnable包裝成Message對(duì)象,并把Runnable設(shè)置給Message的callback字段,然后發(fā)送此消息滴须。

2舌狗、如果msg.callback為空,檢查mCallback是否為空扔水,mCallback是一個(gè)Callback接口痛侍,定義如下:

//Handler.java
public interface Callback {
    public boolean handleMessage(Message msg);
} 

當(dāng)我們這樣:Handler handler = new Handler(callback) 來創(chuàng)建Handler時(shí), mCallback就不為空,它的意義是當(dāng)我們不想派生Handler的子類重寫handleMessage()來處理消息時(shí)铭污,就可以通過Callback來實(shí)現(xiàn)恋日。

3、如果mCallback為空嘹狞,最后調(diào)用Handler的handleMessage方法來處理消息,這就是我們平時(shí)熟悉的處理消息的方法誓竿。

從1磅网、2、3可以看出筷屡,在Handler中涧偷,處理消息的回調(diào)的優(yōu)先級(jí)為:Message的Callback > Handler的Callback > Handler的handleMessage方法

6毙死、消息的回收復(fù)用

1燎潮、消息的復(fù)用

前面多次提到了Message,當(dāng)我們通過Handler的obtainMessage()或Message的obtain()獲取一個(gè)Message對(duì)象時(shí)扼倘,系統(tǒng)并不是每次都new一個(gè)出來确封,而是先從消息池中(sPool)嘗試獲取一個(gè)Message。Handler的obtainMessage()最終是調(diào)用了Message的obtain()再菊,Message的obtain方法如下:

//Message.java
public static Message obtain() {
        synchronized (sPoolSync) {
        //從sPool頭部取出一個(gè)Message對(duì)象返回爪喘,并把消息從鏈表斷開(即把sPool指向下一個(gè)Message)
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0;//清除in-use flag
                sPoolSize--;//消息池的大小進(jìn)行減1操作
                return m;
            }
        }
        //消息池中沒有Message,直接new一個(gè)返回
        return new Message();

sPool的數(shù)據(jù)類型為Message纠拔,通過next成員變量秉剑,維護(hù)一個(gè)消息池,消息池的默認(rèn)大小為50稠诲。定義如下:

public final class Message implements Parcelable {
    public static final Object sPoolSync = new Object();//用于在獲取Message對(duì)象時(shí)進(jìn)行同步鎖
    private static int sPoolSize = 0;//池的大小
    private static final int MAX_POOL_SIZE = 50;//池的可用大小
    private static Message sPool;
    Message next;
    //...
}

雖然叫消息池侦鹏,其實(shí)是通過鏈表實(shí)現(xiàn)的,每個(gè)Message都有一個(gè)同類型的next字段臀叙,這個(gè)next就是指向下一個(gè)可用的Message略水,最后一個(gè)可用的Message的next為空,這樣所有可用的Message對(duì)象就通過next串成一個(gè)Message池匹耕,sPool指向池中的第一個(gè)Message聚请,復(fù)用消息其實(shí)是從鏈表的頭部獲取一個(gè)Message返回

2、消息的回收

我們發(fā)現(xiàn)在obtain方法中新創(chuàng)建Message對(duì)象時(shí)驶赏,并不會(huì)直接把它放到池中再返回炸卑,那么Message對(duì)象是什么時(shí)候被放進(jìn)消息池中的呢?是在回收Message時(shí)把它放入池中煤傍,Message中也有類似Bitmap那樣的recycler函數(shù)盖文,如下:

//Message.java
public void recycle() {
    //判斷消息是否正在使用
        if (isInUse()) {
            if (gCheckRecycle) {//Android 5.0以后的版本默認(rèn)為true,之前的版本默認(rèn)為false.
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();
    }
    
 //對(duì)于不再使用的消息,加入到消息池
 void recycleUnchecked() {
     //將消息標(biāo)示位置為IN_USE蚯姆,并清空消息所有的參數(shù)
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;
        synchronized (sPoolSync) {
           //當(dāng)消息池沒有滿時(shí)五续,將Message對(duì)象加入消息池(即把Message插入鏈表頭部)
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;//消息池的可用大小進(jìn)行加1操作
            }
        }
    }

recycler函數(shù)先判斷該Message是否還在使用,如果還在使用龄恋,就會(huì)拋異常疙驾,否則就調(diào)用recyclerUnchecked函數(shù)根據(jù)MAX_POOL_SIZE判斷是否把該消息回收,回收前還要先清空該消息的各個(gè)字段郭毕,回收消息就是把自身插入到鏈表的表頭它碎。

通過消息的復(fù)用回收,減少M(fèi)essage對(duì)象不斷創(chuàng)建與銷毀的過程显押,提升了效率扳肛。

7、小結(jié)

1乘碑、創(chuàng)建Looper時(shí)挖息,只能通過Looper的prepare方法創(chuàng)建,在創(chuàng)建Looper時(shí)會(huì)在內(nèi)部創(chuàng)建一個(gè)MessageQueue兽肤,并把Looper保存在線程的TLS區(qū)域中套腹,一個(gè)線程只能對(duì)應(yīng)一個(gè)Looper,一個(gè)Looper只能對(duì)應(yīng)一個(gè)MessageQueue轿衔;

2沉迹、在創(chuàng)建MessageQueue時(shí),MessageQueue與NativeMessageQueue建立連接害驹,NativeMessageQueue存儲(chǔ)地址存于MessageQueue的mPtr字段中鞭呕,java層和native通過mPtr字段進(jìn)行通信(native端通過Linux的epoll機(jī)制建立起消息機(jī)制);

3、由于ThreadLocal的作用宛官,Looper屬于某個(gè)線程葫松,而MessageQueue存儲(chǔ)在Looper中,所以MessageQueue則通過Looper與特定的線程關(guān)聯(lián)上底洗,而Handler在構(gòu)造中又與Looper和MessageQueue相關(guān)聯(lián)腋么,當(dāng)我們通過Handler發(fā)送消息時(shí),消息就會(huì)被插入到Handler關(guān)聯(lián)的MessageQueue中亥揖,而Looper會(huì)不斷的輪詢消息珊擂,從MessageQueue中取出消息給相應(yīng)的Handler處理圣勒,所以最終通過Handler發(fā)送的消息就會(huì)被執(zhí)行到Looper所在線程上,這就是Handler線程切換的原理摧扇,無論發(fā)送消息的Handler對(duì)象處于什么線程圣贸,最終處理消息的都是Looper所在線程

4扛稽、Looper從MessageQueue中取出消息后吁峻,會(huì)交給消息的target(Handler)處理,在Handler中在张,處理消息的回調(diào)的優(yōu)先級(jí)為:Message的Callback > Handler的Callback > Handler的handleMessage方法用含;

5、因?yàn)閼?yīng)用程序啟動(dòng)時(shí)在ActivityThread.main方法中的Looper.prepareMainLooper()中已經(jīng)調(diào)用了Looper.prepare(false),所以在主線程中創(chuàng)建Handler無需我們手動(dòng)調(diào)用Looper.prepare()帮匾,而在子線程中啄骇,如果我們不傳遞UI線程所屬的Looper去創(chuàng)建Handler,那么就需要調(diào)用Looper.prepare()后再創(chuàng)建Handle來傳遞消息辟狈,因?yàn)镠andler要和某個(gè)線程中的MessageQueue和Looper關(guān)聯(lián)肠缔,只有調(diào)用Looper.prepare方法,Looper和MessageQueue才屬于某個(gè)線程哼转;

6、消息池是一個(gè)單鏈表槽华,復(fù)用Message時(shí)壹蔓,從頭出取出,如果取不到猫态,則新建返回佣蓉,回收Message時(shí),也從頭插入亲雪。

結(jié)語

本文從Android應(yīng)用UI線程消息循環(huán)的創(chuàng)建勇凭,消息循環(huán)的啟動(dòng),Handler與Looper义辕、MessageQueue的關(guān)聯(lián)虾标,消息的發(fā)送與分發(fā),還有消息的復(fù)用這幾個(gè)角度來講解了Message灌砖,Handler璧函,MessageQueue,Looper之間是如何配合工作基显,在你了解java層的消息機(jī)制是如何運(yùn)作后蘸吓,希望大家去了解一下native的消息機(jī)制诸衔,例如要想知道為什么loop方法是死循環(huán)但卻不會(huì)消耗性能赶么,這些只有native層的消息機(jī)制才能給你答案.

除此之外,我們還知道了MessageQueue的IdleHandler的作用鹃锈,它會(huì)在線程空閑時(shí)工作,還有異步消息的處理宪萄,它的優(yōu)先級(jí)高于同步消息艺谆,會(huì)被優(yōu)先處理,還有它們的應(yīng)用場(chǎng)景雨膨,一般我們是在子線程切換到UI線程時(shí)使用Handler機(jī)制擂涛,但其實(shí)我們也可以在子線程使用Handler機(jī)制,可以參考Android中的HandlerThread聊记,它的底層就是Handler+Thread.

以上就是本文的全部?jī)?nèi)容撒妈,希望大家有所收獲。

參考資料:

Android消息機(jī)制1-Handler

最后編輯于
?著作權(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)離奇詭異挨队,居然都是意外死亡谷暮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門盛垦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湿弦,“玉大人,你說我怎么就攤上這事腾夯〖瞻#” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵蝶俱,是天一觀的道長(zhǎng)班利。 經(jīng)常有香客問我,道長(zhǎng)榨呆,這世上最難降的妖魔是什么罗标? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮愕提,結(jié)果婚禮上馒稍,老公的妹妹穿的比我還像新娘。我一直安慰自己浅侨,他們只是感情好纽谒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著如输,像睡著了一般鼓黔。 火紅的嫁衣襯著肌膚如雪央勒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天澳化,我揣著相機(jī)與錄音崔步,去河邊找鬼。 笑死缎谷,一個(gè)胖子當(dāng)著我的面吹牛井濒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播列林,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼瑞你,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了希痴?” 一聲冷哼從身側(cè)響起者甲,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砌创,沒想到半個(gè)月后虏缸,有當(dāng)?shù)厝嗽跇淞掷锇l(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
  • 文/蒙蒙 一诲宇、第九天 我趴在偏房一處隱蔽的房頂上張望际歼。 院中可真熱鬧,春花似錦姑蓝、人聲如沸鹅心。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旭愧。三九已至颅筋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間输枯,已是汗流浹背议泵。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桃熄,地道東北人先口。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瞳收,于是被迫代替她去往敵國和親碉京。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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