Android啟動流程源碼解析(二)

Android啟動流程源碼解析(二)

在之前的Android啟動流程源碼解析(一)源碼分析中洲劣,我們最后遺留下來一個問題。那就是我們的Activity的啟動是啥時候處理的翱问摺囱稽?上萬行的分析,也沒看到我們想要的onCreate啥的二跋。其實就是resumeFocusedStacksTopActivities方法战惊,所以我們這篇文章就從這個方法開始。

resumeFocusedStacksTopActivities

   boolean resumeFocusedStacksTopActivities() {
        return resumeFocusedStacksTopActivities(null, null, null);
    }

    boolean resumeFocusedStacksTopActivities(ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        boolean result = false;
        if (targetStack != null && (targetStack.isTopStackOnDisplay()|| getTopDisplayFocusedStack() == targetStack)) {
            //******重點方法******如果當前的activitystack正好處于屏幕的頂部扎即,那么直接調(diào)用將target設(shè)置到頂部顯示
            result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }

        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            //標記是否已經(jīng)顯示在屏幕上
            boolean resumedOnDisplay = false;
            final ActivityDisplay display = mActivityDisplays.get(displayNdx);
            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = display.getChildAt(stackNdx);
                //獲取到當前ActivityStack頂部正在運行的Activity
                final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
                if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
                    continue;
                }
                if (stack == targetStack) {
                    //上面已經(jīng)做過resume處理了吞获,所以這里我們就不再做處理了
                    resumedOnDisplay |= result;
                    continue;
                }
                if (display.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
                    stack.executeAppTransition(targetOptions);
                } else {
                    resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
                }
            }
            //如果仍然沒有顯示在屏幕上况凉,那么就獲取到屏幕當前持有焦點的ActivityStack,然后將activity顯示在上面
            if (!resumedOnDisplay) {
                final ActivityStack focusedStack = display.getFocusedStack();
                if (focusedStack != null) {
                    focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
                }
            }
        }
        return result;
    }

這里我們只需要關(guān)注一個方法 resumeTopActivityUncheckedLocked 各拷,這個方法也特別長刁绒,我們就拆分開,只關(guān)注重點方法即可

    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mInResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }
        boolean result = false;
        try {
            // Protect against recursion.
            mInResumeTopActivity = true;
            //***重點關(guān)注******
            result = resumeTopActivityInnerLocked(prev, options);
            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mInResumeTopActivity = false;
        }

        return result;
    }

這里我們也只關(guān)注resumeTopActivityInnerLocked方法

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
       ...  
       if (mResumedActivity != null) {
            //****重點方法 ****** 調(diào)用acitivity的pause方法
            pausing |= startPausingLocked(userLeaving, false, next, false);
        }
        ...
        //進行activity的創(chuàng)建處理
        mStackSupervisor.startSpecificActivityLocked(next, true, false);
        ...
    }

代碼也很長烤黍,我們只提取了兩個比較重要的函數(shù)知市,一個是調(diào)用onPause生命周期函數(shù),另一個是調(diào)用onCreate生命周期函數(shù)的蚊荣。

onPause的暫停過程

我們首先來看一下是如何一步步通過調(diào)度來執(zhí)行onPause的生命周期調(diào)度的

//ActivityStack.java
    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, ActivityRecord resuming, boolean pauseImmediately) {
        ...
        //當前正在顯示的Activity
        ActivityRecord prev = mResumedActivity;
        //當前正在顯示的Activity需要執(zhí)行暫停操作了初狰。
        //將其賦值給mPausingActivity成員變量。
        mPausingActivity = prev;
        mLastPausedActivity = prev;
        //Activity綁定了對應(yīng)的APP互例?難道有不綁定的情況么奢入?
        if (prev.attachedToProcess()) {
               ...  
                //******重點方法****
 mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,prev.configChangeFlags, pauseImmediately));
            ...

重點方法已經(jīng)標注出來了。我們先看看它的參數(shù)的創(chuàng)建過程媳叨。

PauseActivityItem.obtain
//PauseActivityItem.java
    //從池中取出一個PauseActivityItem類
    public static PauseActivityItem obtain(boolean finished, boolean userLeaving, int configChanges, boolean dontReport) {
        PauseActivityItem instance = ObjectPool.obtain(PauseActivityItem.class);
        if (instance == null) {
            instance = new PauseActivityItem();
        }
        instance.mFinished = finished;
        instance.mUserLeaving = userLeaving;
        instance.mConfigChanges = configChanges;
        instance.mDontReport = dontReport;
        return instance;
    }
//ObjectPool.java
    public static <T extends ObjectPoolItem> T obtain(Class<T> itemClass) {
        synchronized (sPoolSync) {
            @SuppressWarnings("unchecked")
            final ArrayList<T> itemPool = (ArrayList<T>) sPoolMap.get(itemClass);
            if (itemPool != null && !itemPool.isEmpty()) {
                return itemPool.remove(itemPool.size() - 1);
            }
            return null;
        }
    }

可以看到腥光,對于PauseActivityItem的獲取,是通過享元模式 來進行處理的糊秆。

回到主干武福。

這的mService是ActivityTaskManagerServicegetLifecycleManager方法返回的是ClientLifecycleManager對象痘番。

    private final ClientLifecycleManager mLifecycleManager;
    mLifecycleManager = new ClientLifecycleManager();
    ClientLifecycleManager getLifecycleManager() {
        return mLifecycleManager;
    }
scheduleTransaction
//ClientLifecycleManager.java
    //調(diào)用一次生命周期的調(diào)度請求
    void scheduleTransaction(@NonNull IApplicationThread client, @NonNull IBinder activityToken,@NonNull ActivityLifecycleItem stateRequest) throws RemoteException {
        final ClientTransaction clientTransaction = transactionWithState(client, activityToken,stateRequest);
        scheduleTransaction(clientTransaction);
    }
    //實際的調(diào)度方法
    void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            //回收
            transaction.recycle();
        }
    }
    //創(chuàng)建一個持有狀態(tài)的事務(wù)類
    private static ClientTransaction transactionWithState(@NonNull IApplicationThread client,
            @NonNull IBinder activityToken, @NonNull ActivityLifecycleItem stateRequest) {
        final ClientTransaction clientTransaction = ClientTransaction.obtain(client, activityToken);
        clientTransaction.setLifecycleStateRequest(stateRequest);
        return clientTransaction;
    }

這里最終會調(diào)用ClientTransaction對象的schedule方法捉片。

//ClientTransaction.java
    public void schedule() throws RemoteException {
        mClient.scheduleTransaction(this);
    }

這里的mClient是一個IApplicationThread,其Server端是ActivityThread#ApplicationThread汞舱。所以最終調(diào)用的是ApplicationThreadscheduleTransaction方法

//ActivityThread.java
    public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            //會調(diào)用ActivityThread.scheduleTransaction方法->該方法位于ActivityThread的父類中
            ActivityThread.this.scheduleTransaction(transaction);
        }

//ClientTransactionHandler.java
    void scheduleTransaction(ClientTransaction transaction) {
        //執(zhí)行預(yù)處理
        transaction.preExecute(this);
        //通過Handler機制發(fā)送事務(wù)請求
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
    }

Handler消息機制就不貼出來了伍纫,直接看其是怎么處理的。

//ActivityThread.java
    private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);

            case EXECUTE_TRANSACTION://執(zhí)行生命周期的調(diào)度工作
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);

當接收到Handler以后昂芜,會調(diào)用TransactionExecutorexecute()方法莹规。

//TransactionExecutor.java
    public void execute(ClientTransaction transaction) {
        final IBinder token = transaction.getActivityToken();
        ...
        //循環(huán)遍歷回調(diào)請求的所有狀態(tài),并在適當?shù)臅r間執(zhí)行它們
        executeCallbacks(transaction);
        //執(zhí)行生命周期的改變
        executeLifecycleState(transaction);
    }

ClientTransaction存在兩種事務(wù),

  • 一種是通過setLifecycleStateRequest設(shè)置一個對象的事務(wù)類型泌神,用于表示事務(wù)執(zhí)行以后良漱,客戶端應(yīng)該處于的生命周期狀態(tài)
  • 一種是addCallback,增加對客戶端的事務(wù)類型回調(diào)欢际,對客戶端一系列的回調(diào)母市。

這兩個不同的類型,在這里就會存在不同的處理方法损趋。對于第一種會在executeCallbacks中進行處理窒篱,第二種則會在executeLifecycleState中進行處理。

我們這兒的暫停,是通過第二種來進行設(shè)置的墙杯,所以我們直接看executeLifecycleState這個方法配并。

//TransactionExecutor.java
    //如果事務(wù)請求,則轉(zhuǎn)換到最終狀態(tài)
    private void executeLifecycleState(ClientTransaction transaction) {
        // ActivityStackSupervisor.java中進行了這個設(shè)置
        // final ActivityLifecycleItem lifecycleItem;
        //                if (andResume) {
        //                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
        //                } else {
        //                    lifecycleItem = PauseActivityItem.obtain();
        //                }
        //                clientTransaction.setLifecycleStateRequest(lifecycleItem);
        //所以這里的lifecycleItem可能是ResumeActivityItem或者PauseActivityItem或者其他的生命周期相關(guān)類
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
        if (lifecycleItem == null) {
            //如果不是通過setLifecycleStateRequest設(shè)置的高镐,那么該方法不需要處理溉旋,直接返回即可
            return;
        }
        //使用適當?shù)膮?shù)執(zhí)行最后的轉(zhuǎn)換
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }

我們這里的lifecycleItem是我們剛才創(chuàng)建的PauseActivityItem,這里會執(zhí)行其execute方法嫉髓。

//PauseActivityItem.java
    @Override
    public void execute(ClientTransactionHandler client, IBinder token,  PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
        //這里的client观腊,是Activity獨享
        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,"PAUSE_ACTIVITY_ITEM");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

這里的client對象我們需要回退去跟蹤一下

//ActivityThread.java
private final TransactionExecutor mTransactionExecutor = new TransactionExecutor(this);
//TransactionExecutor.java
//在ActivityThread類中中調(diào)用了mTransactionExecutor = new TransactionExecutor(this)這個方法,其中的mTransactionHandler是ActivityThread本身
    public TransactionExecutor(ClientTransactionHandler clientTransactionHandler) {
        mTransactionHandler = clientTransactionHandler;
    }

所以說算行,最終調(diào)用的是ActivityThreadhandlePauseActivity方法

//ActivityThread.java
    public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,int configChanges, PendingTransactionActions pendingActions, String reason) {
        //獲取對應(yīng)的ActivityClientRecord對象
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            ...
            //***重點方法***梧油,執(zhí)行pause方法
            performPauseActivity(r, finished, reason, pendingActions);
            ...
        }
    }

    private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
                                        PendingTransactionActions pendingActions) {
        ...
        //****重點方法****
        performPauseActivityIfNeeded(r, reason);
        ...
        return shouldSaveState ? r.state : null;
    }
    private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
            ...
            r.activity.mCalled = false;
            //重點方法,通過Instrumentation調(diào)用onPause生命周期
            mInstrumentation.callActivityOnPause(r.activity);
            ...
    }

最終會通過Instrumentation調(diào)用callActivityOnPause方法州邢。

   //Instrumentation.java
    public void callActivityOnPause(Activity activity) {
        activity.performPause();
    }
    //Activity.java
    final void performPause() {
        dispatchActivityPrePaused();
        mDoReportFullyDrawn = false;
        //管理的Fragment的處理
        mFragments.dispatchPause();
        mCalled = false;
        //調(diào)用了onPause生命周期方法
        onPause();
        writeEventLog(LOG_AM_ON_PAUSE_CALLED, "performPause");
        //設(shè)置mResumed為false儡陨,表示當前activity沒有展示
        mResumed = false;
        //調(diào)用一些回調(diào)函數(shù)
        dispatchActivityPostPaused();
    }

到這里為止,原來在我們面前展示的那個Activity調(diào)用了其onPause方法量淌。

Activity的創(chuàng)建過程

回到主線的resumeTopActivityInnerLocked方法中骗村,當執(zhí)行完startPausingLocked方法后,會調(diào)用mStackSupervisor.startSpecificActivityLocked方法

    //ActivityStackSupervisor.java
    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
        //根據(jù)uid和pid呀枢,獲取activity對應(yīng)的進行和線程信息
        final WindowProcessController wpc =mService.getProcessController(r.processName, r.info.applicationInfo.uid);
        boolean knownToBeDead = false;
        if (wpc != null && wpc.hasThread()) {
            //如果進程和線程都存在胚股,執(zhí)行后面的代碼
            try {
                realStartActivityLocked(r, wpc, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            knownToBeDead = true;
        }
            //通過message進行進程的啟動。
            final Message msg = PooledLambda.obtainMessage(
                    ActivityManagerInternal::startProcess, mService.mAmInternal, r.processName,
                    r.info.applicationInfo, knownToBeDead, "activity", r.intent.getComponent());
            mService.mH.sendMessage(msg);
        } finally {
            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
        }
    }

如果要啟動的activity所在的進程和線程都存在裙秋,那么直接調(diào)用realStartActivityLocked方法進行啟動琅拌,否則的話,就會調(diào)用Handler機制進行進程的創(chuàng)建摘刑。

realStartActivityLocked

    //真正執(zhí)行Activity啟動的方法
    boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc, boolean andResume, boolean checkConfig) throws RemoteException {
        //如果還有activity沒有暫停进宝,這里會直接返回false
        if (!mRootActivityContainer.allPausedActivitiesComplete()) {
            return false;
        }

        final TaskRecord task = r.getTaskRecord();
        final ActivityStack stack = task.getStack();
        //設(shè)置標志位,不再接收其他activity的resume的操作
        beginDeferResume();
        try {
           ...
                //創(chuàng)建了一個Activity事務(wù)
                final ClientTransaction clientTransaction = ClientTransaction.obtain(proc.getThread(), r.appToken);
                final DisplayContent dc = r.getDisplay().mDisplayContent;
                //增加一個要執(zhí)行的事務(wù)LaunchActivityItem泣侮。
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                        r.icicle, r.persistentState, results, newIntents,
                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
                                r.assistToken));
                final ActivityLifecycleItem lifecycleItem;
                //設(shè)置其生命周期LifecycleStateRequest
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);
                //執(zhí)行事務(wù)的調(diào)度
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
        ...
        proc.onStartActivity(mService.mTopProcessState, r.info);
        return true;
    }

在這個方法里面即彪,創(chuàng)建了一個事務(wù)紧唱,在事務(wù)中增加了一個callback回調(diào)活尊,然后通過setLifecycleStateRequest設(shè)置了一個生命周期,最后通過scheduleTransaction執(zhí)行了調(diào)度漏益。在前面的onPause中蛹锰,我們梳理了整個調(diào)度的流程,最后會調(diào)用到LaunchActivityItemexecute绰疤,然后會調(diào)用生命周期所對應(yīng)的ResumeActivityItemexecute铜犬。

我們挨個看,先看LaunchActivityItem的調(diào)用

//LaunchActivityItem.java
    //一般會在TransactionExecutor中調(diào)用這個方法
    //ClientTransactionHandler實際是ActivityThread對象,所以這里會執(zhí)行activitythread類中的handleLaunchActivity方法
    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
                        PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
                mPendingResults, mPendingNewIntents, mIsForward,
                mProfilerInfo, client, mAssistToken);
        //調(diào)用activitythread類中的handleLaunchActivity方法
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }

其本質(zhì)調(diào)用的是ActivityThread中的handleLaunchActivity方法癣猾。

這部分的功能敛劝,我們在Android布局窗口繪制分析中進行過解析。這里不再往下進行解析了纷宇。

我們直接跳過這部分夸盟,來看看如果啟動的activity所在的進程和線程都存在。會進行進程的創(chuàng)建工作像捶。這部分我們后面專門再進行一篇關(guān)于進程創(chuàng)建的解析工作上陕。

本文由 開了肯 發(fā)布!

同步公眾號[開了肯]

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拓春,一起剝皮案震驚了整個濱河市释簿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌硼莽,老刑警劉巖庶溶,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沉删,居然都是意外死亡渐尿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門矾瑰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來砖茸,“玉大人,你說我怎么就攤上這事殴穴×购唬” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵采幌,是天一觀的道長劲够。 經(jīng)常有香客問我,道長休傍,這世上最難降的妖魔是什么征绎? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮磨取,結(jié)果婚禮上人柿,老公的妹妹穿的比我還像新娘。我一直安慰自己忙厌,他們只是感情好凫岖,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逢净,像睡著了一般哥放。 火紅的嫁衣襯著肌膚如雪歼指。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天甥雕,我揣著相機與錄音踩身,去河邊找鬼。 笑死社露,一個胖子當著我的面吹牛惰赋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呵哨,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼赁濒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孟害?” 一聲冷哼從身側(cè)響起拒炎,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎挨务,沒想到半個月后击你,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡谎柄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年丁侄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朝巫。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸿摇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出劈猿,到底是詐尸還是另有隱情拙吉,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布揪荣,位于F島的核電站筷黔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏仗颈。R本人自食惡果不足惜佛舱,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挨决。 院中可真熱鬧请祖,春花似錦、人聲如沸凰棉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撒犀。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間或舞,已是汗流浹背荆姆。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留映凳,地道東北人胆筒。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像诈豌,于是被迫代替她去往敵國和親仆救。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344