Android應(yīng)用啟動(dòng)過程

前言:

最近發(fā)現(xiàn)自己好像做了android這么久,竟然還不知道一個(gè)應(yīng)用是如何去啟動(dòng)的,所以決定去一探究竟,結(jié)果發(fā)現(xiàn)這個(gè)過程好像有點(diǎn)難,好像有點(diǎn)繁雜,畢竟我以前從未接觸過framework層的內(nèi)容。其實(shí)我也是一面探究一面來寫這篇文章的。沒辦法,畢竟我又不會(huì),而且在開發(fā)應(yīng)用層的時(shí)候也沒怎么接觸過。但是這東西內(nèi)容太多了,如果我不寫點(diǎn)東西的話長時(shí)間不接觸肯定會(huì)忘記,所以決定一面探究這個(gè)過程一面寫下這篇文章。最后肯定會(huì)有寫得不好或者我理解錯(cuò)的地方,還希望有大神能指點(diǎn)一二。
注意,這里只講啟動(dòng)是怎么樣的一個(gè)流程,而不講具體怎么去實(shí)現(xiàn)


先看看一些基礎(chǔ)的概念:

Linux進(jìn)程通信做了什么事

(1)數(shù)據(jù)傳輸
(2)資源和數(shù)據(jù)共享
(3)通知
(4)進(jìn)程控制

Linux進(jìn)程通信的方式

了解一下就行,至于詳哪種方式用于哪種場景,我也不是很清楚。
(1)管道
(2)信號(hào)量
(3)消息隊(duì)列
(4)信號(hào)
(5)共享內(nèi)存
(6)套接字

IPC機(jī)制

什么是IPC,好像接觸安卓的時(shí)候經(jīng)常能聽到IPC但是又不知道是什么,IPC的全稱是Inter-Process Communication,就是指進(jìn)程間的通信,那么IPC機(jī)制可以簡單的理解為就是進(jìn)程間通信的機(jī)制。

一.應(yīng)用啟動(dòng)過程涉及到的內(nèi)容

首先肯定要知道這個(gè)過程涉及到哪些東西,才好梳理出整個(gè)流程。我也是加班加點(diǎn)的看了很多文章和博客,下面說說我的看法。
我們都知道在android中每個(gè)應(yīng)用都可以當(dāng)做一個(gè)進(jìn)程,那么應(yīng)用的啟動(dòng)過程無疑會(huì)涉及到進(jìn)程通信
據(jù)我了解,這個(gè)過程大致涉及到3個(gè)進(jìn)程:
(1)Launcher 也就是桌面,可以把我們的手機(jī)桌面當(dāng)成一個(gè)進(jìn)程
(2)systemserver就是所有的服務(wù),可以當(dāng)成是手機(jī)開機(jī)之后系統(tǒng)啟動(dòng)的一個(gè)進(jìn)程
(3)zygote進(jìn)程,可以當(dāng)成是一個(gè)創(chuàng)建進(jìn)程的進(jìn)程,好像也是開機(jī)后啟動(dòng)的

那么整個(gè)過程就是這3個(gè)進(jìn)程間用IPC機(jī)制進(jìn)行通信的過程,所以說設(shè)計(jì)到的內(nèi)容大概會(huì)有:
(1)上面提的3個(gè)進(jìn)程
(2)Android進(jìn)程通信會(huì)用到的Binder機(jī)制
(3)Android進(jìn)程通信會(huì)用到的AIDL
我大概就是這樣理解的,詳細(xì)的下面會(huì)說,不過會(huì)按我的思路去說。

二.點(diǎn)擊Launcher 中的圖標(biāo)后發(fā)生的事

先看看點(diǎn)擊桌面的圖標(biāo)后會(huì)發(fā)生什么事情,我在網(wǎng)上找到文章這樣寫(當(dāng)然他是貼源碼的,源碼我這就先不貼)



那么我是不是可以把這個(gè)過程看成這樣


那么是不是可以看出點(diǎn)擊桌面圖標(biāo)之后其實(shí)最后是調(diào)用了我們熟悉的startActivity方法。
到這里,我打算先不研究Android應(yīng)用啟動(dòng)過程,不如我先研究startActivity,也就是一個(gè)頁面跳轉(zhuǎn)到另一個(gè)頁面的過程。

三.startActivity的過程

找文章,看源碼,發(fā)現(xiàn)startActivity的過程是這樣的。(別人貼的代碼,我就先直接拿來用了)

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {  
        if (mParent == null) {//只要關(guān)心mParent==null的情況就可以了  
            Instrumentation.ActivityResult ar =  
                mInstrumentation.execStartActivity(  
                    this, mMainThread.getApplicationThread(), mToken, this,  
                    intent, requestCode, options);  
            .........  
        } else {  
            ......  
        }  
    }  

不需要看全部代碼,正如我所說,我們的目的是先探索過程,而不是探索具體的實(shí)現(xiàn)。
在這只要知道startActivity內(nèi)部調(diào)用一個(gè)Instrumentation類的execStartActivity方法就行。
startActivity內(nèi)部調(diào)用一個(gè)Instrumentation類的execStartActivity方法
startActivity內(nèi)部調(diào)用一個(gè)Instrumentation類的execStartActivity方法
startActivity內(nèi)部調(diào)用一個(gè)Instrumentation類的execStartActivity方法
重要的事說三遍。然后這里又新涉及到了一個(gè)Instrumentation

它是做啥子的最好肯定是看官方的注釋:
instrumentation can load both a test package and the application under test into the same process. Since the application components and their tests are in the same process, the tests can invoke methods in the components, and modify and examine fields in the components.

其實(shí)我還是不太看得懂它是做什么的,暫時(shí)就先不管它,當(dāng)成是一個(gè)中介就行。直接點(diǎn)進(jìn)看execStartActivity這個(gè)方法(還是別人貼的代碼)

public ActivityResult execStartActivity(  
       Context who, IBinder contextThread, IBinder token, Activity target,  
       Intent intent, int requestCode) {  
                               ......  
       try {  
       //ActivityManagerNative.getDefault()實(shí)際返回的是一個(gè)ActivityManagerProxy對象,也就是AMS的代理  
           int result = ActivityManagerNative.getDefault()  
               .startActivity(whoThread, intent,  
                       intent.resolveTypeIfNeeded(who.getContentResolver()),  
                       null, 0, token, target != null ? target.mEmbeddedID : null,  
                       requestCode, false, false);  
           checkStartActivityResult(result, intent);  
       } catch (RemoteException e) {  
       }  
       return null;  
   }  

發(fā)現(xiàn)調(diào)用的是ActivityManagerNative.getDefault().startActivity()這個(gè)方法,那就涉及到了ActivityManagerNative,其實(shí)也就是涉及到了AMS這個(gè)服務(wù)。
AMS(ActivityManagerService)就是systemserver中的一個(gè)服務(wù)
AMS(ActivityManagerService)就是systemserver中的一個(gè)服務(wù)
AMS(ActivityManagerService)就是systemserver中的一個(gè)服務(wù)

看到這里,會(huì)發(fā)現(xiàn)多出個(gè)AMS,其實(shí)這個(gè)服務(wù)很重要,你可以暫時(shí)看成是操作管理Activity的(下面再介紹它),既然是systemserver的,那么是不是可以說就用到了進(jìn)程通信。
其實(shí)到這里我就有點(diǎn)不了解,你們想想,同一個(gè)進(jìn)程里面的兩個(gè)頁面的跳轉(zhuǎn)為什么要用到另一個(gè)進(jìn)程,直接在這個(gè)進(jìn)程里面做操作不行嗎?
我的理解是這樣的,其實(shí)管理頁面的是AMS,如果要在一個(gè)進(jìn)程內(nèi)做跳轉(zhuǎn)的操作,是不是每個(gè)進(jìn)程都要有AMS,那缺點(diǎn)就很明顯了,所以谷歌要把AMS提出來放到一個(gè)進(jìn)程里面供所有的進(jìn)程去使用。

四.AMS

如果要直接接著上邊的代碼去看頁面之間的跳轉(zhuǎn)的話不太好看懂,因?yàn)樯婕暗搅薃ctivityManagerService這個(gè)服務(wù)和進(jìn)程間的通信,所以至少我覺得我們要先把這兩個(gè)內(nèi)容大概了解一下才能看懂下面的操作,先簡單說說AMS再談通信。

1.AMS是什么

ActivityManagerService,人如其名,管理Activity的服務(wù),但其實(shí)不單只有Activity,應(yīng)該是四大組件。

2.AMS做了什么


這個(gè)出自http://www.lxweimin.com/p/47eca41428d6,功能肯定很多,反正它最主要的肯定是實(shí)現(xiàn)了“Manager”的功能。

3.AMS怎么實(shí)現(xiàn)的

ActivityManagerNative 繼承了Binder 類,這就是接下來我想介紹的Binder機(jī)制。

五.Binder 機(jī)制

大概了解下AMS之后再來看看Binder 是如何實(shí)現(xiàn)進(jìn)程間的通信,Binder 是Android的一種IPC,當(dāng)然Android是基于Linux的,所以它本身也能使用Linux的進(jìn)程IPC。
那個(gè)這個(gè)Binder我就講講我的一些簡單的理解,畢竟沒用過,也不是很懂。

(1)首先Binder的設(shè)計(jì)是基于C/S模型的,很容易想到我們的普通請求網(wǎng)絡(luò)的情況也是基于C/S,而且好像網(wǎng)絡(luò)請求也是一個(gè)進(jìn)程間通信的過程。所以你也可以把Binder通信想成一個(gè)請求網(wǎng)絡(luò)的過程。
(2)涉及到三個(gè)比較主要的部分,C/S中的Client和Server,還有一個(gè)ServiceManager。

拋開所有細(xì)節(jié),總體的通信流程大概就是這樣的。


其實(shí)在這個(gè)過程中我們在稍微擴(kuò)展一點(diǎn)點(diǎn)的細(xì)節(jié),就是Server會(huì)注冊到ServiceManager中,然后Client調(diào)用是去查詢ServiceManager中的Server。比如Client進(jìn)程想要調(diào)用Server進(jìn)程的object對象的一個(gè)方法add。(我這里是引用了別人寫的文章http://weishu.me/2016/01/12/binder-index-for-newer/

這里我要說明一點(diǎn),我這里是因?yàn)橐郧翱催^了代理模式所以比較好理解,如果不知道代理模式的話可能不太能明白這個(gè)返回的proxy是干啥子用的。

簡單來說代理模式模式就是一個(gè)原本類的代理類。我想實(shí)現(xiàn)add功能,我讓代理類來做,代理類內(nèi)部會(huì)自己用某種方法調(diào)用原本類的add功能,如果還是看不懂的話建議可以先去了解一下這個(gè)設(shè)計(jì)模式。

有點(diǎn)說偏了,再看看圖 ,其實(shí)這個(gè)查詢的過程目的為了拿到某個(gè)東西之后能調(diào)用Server中的方法,因?yàn)橐话阄覀儧]辦法跨進(jìn)程調(diào)直接調(diào)用其它進(jìn)程類的方法,所以這里借助了Binder驅(qū)動(dòng)和代理類來實(shí)現(xiàn)
好好想想這個(gè)過程,你要調(diào)用某個(gè)方法,肯定要拿到這個(gè)類的對象,然后對象再調(diào)用方法吧,這個(gè)圖就是拿對象的一個(gè)過程。

我還沒說完,而這個(gè)拿對象的過程嘛,其實(shí)進(jìn)程間就算你要直接拿代理也不可能實(shí)現(xiàn),而這個(gè)實(shí)現(xiàn)的過程還是通過Binder驅(qū)動(dòng),而底層用的肯定不是java去寫,所以暫時(shí)不用關(guān)心,但是既然用了底層Binder驅(qū)動(dòng)對吧,那就肯定在上層會(huì)有個(gè)規(guī)范,所以Server就是所謂的Binder類,而Client端獲取的就是BinderProxy
那這里是不是可以簡單的解釋ActivityManagerNative 繼承Binder 就是為了要用Binder驅(qū)動(dòng)來實(shí)現(xiàn)通信。

OK,就這樣簡單講講就行了,再深入我也不是很懂,而且我目前只是為了看流程而不是為了看實(shí)現(xiàn)。現(xiàn)在你只是心里大概了解了Binder機(jī)制進(jìn)行進(jìn)程間通信的一個(gè)過程。那就跳回execStartActivity方法,回頭看看頁面跳轉(zhuǎn)到頁面間的操作。

六.頁面間跳轉(zhuǎn)與Binder機(jī)制的聯(lián)系

為了方便,我再貼一次上邊execStartActivity方法的代碼

public ActivityResult execStartActivity(  
       Context who, IBinder contextThread, IBinder token, Activity target,  
       Intent intent, int requestCode) {  
                               ......  
       try {  
       //ActivityManagerNative.getDefault()實(shí)際返回的是一個(gè)ActivityManagerProxy對象,也就是AMS的代理  
           int result = ActivityManagerNative.getDefault()  
               .startActivity(whoThread, intent,  
                       intent.resolveTypeIfNeeded(who.getContentResolver()),  
                       null, 0, token, target != null ? target.mEmbeddedID : null,  
                       requestCode, false, false);  
           checkStartActivityResult(result, intent);  
       } catch (RemoteException e) {  
       }  
       return null;  
   }  

我們所在的這個(gè)應(yīng)用里面,是沒法直接拿到AMS對象的,所以這里通過ActivityManagerNative.getDefault() 能獲取到AMS的代理對象,這是ActivityManagerProxy類的對象。獲取的過程就是內(nèi)部通過Binder進(jìn)制,先不研究代碼,反正你知道這句話能拿到AMS的代理,而且通過上面我說的Binder機(jī)制你也知道拿到代理是為了做什么,那就行了。

獲取代理之后發(fā)現(xiàn)它調(diào)用代理的這個(gè)方法



這個(gè)過程中,我們可以簡單看看這個(gè)代理類中的startActivity方法,就看看,不深入(還是別人貼的代碼)

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

看到這里可以了解里面主要操作了兩個(gè)Parcel 類的對象data 和reply,簡單查查Parcel 是啥玩意。簡單的理解這個(gè)就是用于Binder通信的一個(gè)對象,反正先不管它底層到底做了什么,既然是代理模式,就說明這里調(diào)用了startActivity方法,他的內(nèi)部肯定會(huì)調(diào)用原本類中的startActivity方法,而這個(gè)原本類就是ActivityManagerService(AMS),所以我們可以直接去看看AMS的startActivity方法。

里面調(diào)用了很多的方法,我就不全貼了,而且代碼我也沒認(rèn)真去了解,例如可以找到里面有調(diào)用一個(gè)startActivityUncheckedLocked方法(還是別人的代碼)


可以看到這里用到了ActivityStack,所以想想就中的里面肯定是對Activity的棧做了一大堆操作,然后再去調(diào)用activity的生命周期。

其實(shí)這里的AMS是用了雙向的,因?yàn)镃/S結(jié)構(gòu)是單向的,比如網(wǎng)絡(luò)請求,你只能客戶端發(fā)起請求,這里是服務(wù)端也可以發(fā)起請求,具體怎么做先不說了,我是有很多細(xì)節(jié)沒說,但從我上面說的流程能知道頁面間的跳轉(zhuǎn)總體是如何使用實(shí)現(xiàn)的,接下來就會(huì)到原本的問題,應(yīng)用啟動(dòng)流程

七.啟動(dòng)新進(jìn)程

最開始說了點(diǎn)擊Launcher 的圖標(biāo)其實(shí)就是調(diào)用了startActivity,再加上上面的解釋,我們這里就可以說點(diǎn)擊Launcher 的圖標(biāo)后,現(xiàn)在已經(jīng)跳到了AMS里面,所以我們只需要接著在AMS里面找到是如何啟動(dòng)新應(yīng)用的就行。

也就說說我們在上面其實(shí)已經(jīng)知道了怎么從Launcher 進(jìn)程調(diào)到AMS所在的進(jìn)程,現(xiàn)在我們要探究它內(nèi)部是怎么從AMS所在的進(jìn)程跳到zygote進(jìn)程,因?yàn)樽铋_始我說了zygote就是用了創(chuàng)建進(jìn)程的,所以AMS和zygote這兩個(gè)進(jìn)程的通信的流程是怎么樣的

畢竟我是看了很多文章,對照了別人的文章和我自己的理解,我找到了一張很容易理解這個(gè)過程的圖(出自https://blog.csdn.net/ccjhdopc/article/details/52893738


這圖我只截了一部分,因?yàn)槲覀冎毙枰獣簳r(shí)看AMS怎么走流程走到zygote的(這里的SystemServer就是AMS所在的進(jìn)程,最上邊我有講)。

可以看出我們上面講頁面與頁面間的跳轉(zhuǎn)時(shí)正好對用圖中的


在AMS內(nèi)部執(zhí)行startactivity之后會(huì)再執(zhí)行一個(gè)schedulePauseActivity方法,也是用了Binder,不過這回AMS屬于客戶端所以它拿到服務(wù)端的ApplicationThread的代理類ApplicationThreadProxy來做操作,那自然會(huì)調(diào)用原本應(yīng)用進(jìn)程中的ApplicationThread類的schedulePauseActivity方法
這個(gè)方法可以從圖中看出其實(shí)就是做了兩步操作
(1)讓當(dāng)前的activity執(zhí)行onPause生命周期
(2)再用Binder調(diào)用activityPaused方法通知AMS當(dāng)前的activity已經(jīng)onPause了,你可以啟動(dòng)新的activity了

我看了一些代碼,沒有找到在activityPaused方法之后是在哪里判斷進(jìn)程是否已經(jīng)創(chuàng)建,如果有大神知道,麻煩請告訴我一下
反正就是如果進(jìn)程沒被創(chuàng)建會(huì)調(diào)用startProcessLocked方法去調(diào)用Zygote創(chuàng)建新進(jìn)程,不過好像這兩個(gè)進(jìn)程間的通信不是用Binder而是用socket

至于Zygote是如何去創(chuàng)建新進(jìn)程的,這里就先不管,反正它能通過某種方式去創(chuàng)建進(jìn)的進(jìn)程,然后新的進(jìn)程中又通過Binder和AMS進(jìn)行通信,大概是告訴AMS它已經(jīng)創(chuàng)建完成,這時(shí)AMS就再去調(diào)用新的入口activity的生命周期,我的理解大概是這樣的。

八.總結(jié)

先對流程做下總結(jié),可以看出所有的邏輯操作都是由AMS來做的,而創(chuàng)建進(jìn)程的操作是有zygote來做的,而應(yīng)用進(jìn)程與AMS的通信都是使用Binder機(jī)制來進(jìn)行。這篇文章主要是簡單的探究應(yīng)用的啟動(dòng)的過程,所以沒有放太多的代碼,也沒有深入去講,因?yàn)槲也皇呛苣芸炊瓵MS里面的代碼,AIDL也沒有講。
AMS和zygote還有AIDL我想之后單獨(dú)分出來寫文章,特別是AMS,看了一下它的代碼,感覺真的不是三言兩語能夠講清楚的,我是真的感覺有點(diǎn)難。寫這篇文章的目的最主要的還是為了做個(gè)比較,感覺framework的東西不再像寫自定義view這些這么好理解了,所以還是多做點(diǎn)筆記比較好,如果有理解錯(cuò)的地方和寫得不到位的地方,還望大神指點(diǎn)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評(píng)論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評(píng)論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,694評(píng)論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評(píng)論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評(píng)論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評(píng)論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,668評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評(píng)論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,846評(píng)論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評(píng)論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,394評(píng)論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,635評(píng)論 2 380

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