Activity启动流程

最近在研究启动优化方面的知识,那么对于 Activity 的启动过程这方面的知识自然是逃不掉的,那么废话不多说,我们开始梳理。

(文中基于sdk28, 不少有代码片段的地方,可能比较枯燥,我们只要把握主要流程即可~)

1. 启动

启动包括冷启动和热启动,本篇我们主要围绕冷启动做简述。(还不清楚冷启动和热启动的区别,就自行Google哦)

2. 点击一个应用图标

当我们在手机桌面开始点击一个应用图标的时候,此时应用就启动起来了,那么背后究竟发生了什么事,我们一起来看一下:

2.1 startActivity

Activity#startActivity

@Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {//不传 options为null
            startActivityForResult(intent, -1, options);
        } else {
            startActivityForResult(intent, -1);
        }
    }

Activity#startActivityForResult

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            Instrumentation.ActivityResult ar = -----------//注释1
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);        
          ....
        }
    }

注释1处: mInstrumentationInstrumentation 类型,调用了 execStartActivity 方法,其中 mMainThread.getApplicationThread() 需要我们重点关注。

mMainThread -----> ActivityThread 类型
getApplicationThread() 返回 ApplicationThread 类型, 而 ApplicationThread 则是 ActivityThread 的一个内部类,大致如下:

class ActivityThread{
...
  public ApplicationThread getApplicationThread()
    {
        return mAppThread;
    }
  private class ApplicationThread extends IApplicationThread.Stub {
            .....
  }
...
}

OK ,直到了 ApplicationThread 我们继续,调用了 Instrumentation.execStartActivity 我们需要点进去看一下:

2.2 Instrumentation#execStartActivity

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread; //----->注释1
        ...
   
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService() //------->注释2
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
       ...
        return null;
    }

注释1: 其中我们上面说的 ApplicationThread 被转化成 IApplicationThread 是一个 IBinder 类型 , 因为ApplicationThread继承了 IApplicationThread.Stub 是不是很眼熟,类似于我们那个aidl 自动生成的那个代码,你可以认为是代理类。

接着往下,注释2: ActivityManager.getService 返回 IActivityManager 类型 ,看下面代码:

 public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

    private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
            };

ServiceManager 获取到服务,其实就是我们的 ActivityManagerService 对象,它继承了IActivityManager.Stub 为具体实现类

public class ActivityManagerService extends IActivityManager.Stub..{
  ....
}

最终调用实际是 ActivityManagerService 的 startActiviy方法,通过获取远程服务获取到代理类,调用了远程服务,此时由客户端进程转换到远程服务端进程

2.3 ActivityManagerService#startActivity

ActivityManagerService 这里简称 AMS,这里代码就比较多了,我们只看一些对流程上比较重要的方法:

@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

里面可以看到又调用了 startActivityAsUser() 方法,继续走:

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
            boolean validateIncomingUser) {
           ...
        // TODO: Switch to user app stacks here.
        return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
                .setCaller(caller)
                .setCallingPackage(callingPackage)
                .setResolvedType(resolvedType)
                .setResultTo(resultTo)
                .setResultWho(resultWho)
                .setRequestCode(requestCode)
                .setStartFlags(startFlags)
                .setProfilerInfo(profilerInfo)
                .setActivityOptions(bOptions)
                .setMayWait(userId)
                .execute();
    }

只看到最后调用了一个 execute() 方法,那么看是谁的 execute() 方法, obtainStarter() 方法返回 ActivityStarter 类型的对象,那么就是它的,走进去看看:
ActivityStarter#execute

int execute() {
        try {
            if (mRequest.mayWait) {
                return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                        mRequest.callingPackage, mRequest.intent, mRequest.resolvedType,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                        mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                        mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup);
            } else {
                return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                        mRequest.resolvedType, mRequest.activityInfo, mRequest.resolveInfo,
                        mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                        mRequest.resultWho, mRequest.requestCode, mRequest.callingPid,
                        mRequest.callingUid, mRequest.callingPackage, mRequest.realCallingPid,
                        mRequest.realCallingUid, mRequest.startFlags, mRequest.activityOptions,
                        mRequest.ignoreTargetSecurity, mRequest.componentSpecified,
                        mRequest.outActivity, mRequest.inTask, mRequest.reason,
                        mRequest.allowPendingRemoteAnimationRegistryLookup);
            }
        } finally {
            onExecutionComplete();
        }
    }

这个 mRequest.mayWaittrue ,因为前面我们在启动的时候设置了:

image.png

image.png

所以我们就可以直接看 startActivityMayWait 这个方法了。

这个方法就比较长了,我看了好长时间,看到最后方法返回 res,自然而然就比较关注这个 res 赋值是什么,赋值我拿了出来:

 private int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
            int userId, TaskRecord inTask, String reason,
            boolean allowPendingRemoteAnimationRegistryLookup) {
      ....
              final ActivityRecord[] outRecord = new ActivityRecord[1];
              int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason,
                    allowPendingRemoteAnimationRegistryLookup);
              return res;
      ....
}

这个 startActivity 继续调用调用:

image.png

方法里最终 return 又还是继续调用 startActivity 方法 :
image.png

代码如下:

private int startActivity(final ActivityRecord r, ActivityRecord sourceRecord,
                IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                ActivityRecord[] outActivity) {
        int result = START_CANCELED;
        try {
            mService.mWindowManager.deferSurfaceLayout();
            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
                    startFlags, doResume, options, inTask, outActivity);
        } finally {
            // If we are not able to proceed, disassociate the activity from the task. Leaving an
            // activity in an incomplete state can lead to issues, such as performing operations
            // without a window container.
            final ActivityStack stack = mStartActivity.getStack();
            if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
                stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
                        null /* intentResultData */, "startActivity", true /* oomAdj */);
            }
            mService.mWindowManager.continueSurfaceLayout();
        }

        postStartActivityProcessing(r, result, mTargetStack);

        return result;
    }

因为最后返回 result 所以我们还是看 result 的赋值, 调用了 startActivityUnchecked 方法 最后返回 START_SUCCESS 应该就是最后的方法了吧,目测感觉应该要到头了,看了一下这个方法有点长:

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
            ActivityRecord[] outActivity) {
        ....
        //把目标栈移动到前台
       reusedActivity = setTargetStackAndMoveToFrontIfNeeded(reusedActivity);

if (dontStart) {
          
            topStack.mLastPausedActivity = null;
            if (mDoResume) {
                mSupervisor.resumeFocusedStackTopActivityLocked();//--------注释1
            }           
          ...
          result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);//-------注释2
        }
        ....



        return START_SUCCESS;
}

注释1:ActivityStackSupervisor 负责将目标 Activity 放于栈顶 ;
注释2:复用或者创建新的任务栈 ;

2.4 ActivityStackSupervisor#resumeFocusedStackTopActivityLocked

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

    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {

        if (!readyToResume()) {
            return false;
        }

        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }

        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null || !r.isState(RESUMED)) {
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
        } else if (r.isState(RESUMED)) {
            // Kick off any lingering app transitions form the MoveTaskToFront operation.
            mFocusedStack.executeAppTransition(targetOptions);
        }

        return false;
    }

result = resumeTopActivityInnerLocked(prev, options)
这个方法就比较长,参考其它资料,里面比较重要的就是:

.....
if (next.app != null && next.app.thread != null) {
  //把要启动Activity进程信息封装在一个 ClientTransaction 对象中了
final ClientTransaction transaction = ClientTransaction.obtain(next.app.thread,
                            next.appToken);
//如果要启动的activity已经存在,就该resume了
mStackSupervisor.scheduleResumeTopActivities();
}else{
      //如果进程不存在,就要启动新的Activity
     mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
....

假如是第一次启动 Activity 就执行 mStackSupervisor.startSpecificActivityLocked() 方法,继续跟进去:

void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        getLaunchTimeTracker().setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

继续执行那就是 realStartActivityLocked() 方法:

 final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {

  ....
      // Create activity launch transaction. 创建启动Activity的事务
                final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
                        r.appToken);
                //回调callback 是 LaunchActivityItem
                clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
                        System.identityHashCode(r), r.info,
                        mergedConfiguration.getGlobalConfiguration(),
                        mergedConfiguration.getOverrideConfiguration(), r.compat,
                        r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
                        r.persistentState, results, newIntents, mService.isNextTransitionForward(),
                        profilerInfo));
                 final ActivityLifecycleItem lifecycleItem;
                if (andResume) {
                    lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
                } else {
                    lifecycleItem = PauseActivityItem.obtain();
                }
                clientTransaction.setLifecycleStateRequest(lifecycleItem);

                // Schedule transaction. 最后开始执行了
                mService.getLifecycleManager().scheduleTransaction(clientTransaction);
  ....
}

这里可以看到生命周期的回调被封装成一个一个的 Item ,例如 : LaunchActivityItem , ResumeActivityItem , PauseActivityItem 光看名字也能大概猜出一点来。

这里 mService 就是 AMS , getLifecycleManager() 获取到 ClientLifecycleManager 对象 , 调用 scheduleTransaction 方法 ;

2.5 ClientLifecycleManager#scheduleTransaction

void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
        final IApplicationThread client = transaction.getClient();
        transaction.schedule();
        if (!(client instanceof Binder)) {
            // If client is not an instance of Binder - it's a remote call and at this point it is
            // safe to recycle the object. All objects used for local calls will be recycled after
            // the transaction is executed on client in ActivityThread.
            transaction.recycle();
        }
    }

2.6 ClientTransaction#schedule

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

mClientIApplicationThread 就是 ApplicationThread , 而 ApplicationThreadActivityThread 的一个内部类,执行了 scheduleTransaction 方法。

此时你会发现,由原来的 Client 进程 ---------> AMS进程 ,现在又回到了 客户端进程了。

Client 进程 ---------> AMS进程 ---------> Client 进程

2.7 ActivityThread#ApplicationThread # scheduleTransaction

public final class ActivityThread extends ClientTransactionHandler {
@Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }
}

因为 ActivityThread 继承了 ClientTransactionHandler ,在父类中实现了这个方法:

2.8 ClientTransactionHandler#scheduleTransaction

void scheduleTransaction(ClientTransaction transaction) {
        transaction.preExecute(this);
        sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}

终于看到点能看懂的代码了,开始切换主线程了,因为看到了 sendMessage , 哈哈。
发送了一个 ActivityThread.H.EXECUTE_TRANSACTION 消息 ,objtransaction , 继续到 ActivityThread 中看下:

2.9 ActivityThread#mH

public final class ActivityThread extends ClientTransactionHandler{

  //内部类H
 class H extends Handler {
      //很多消息类型,找到我们刚才发的消息类型 为159
     public static final int BIND_APPLICATION        = 110;
        public static final int EXIT_APPLICATION        = 111;
        public static final int RECEIVER                = 113;

      public static final int EXECUTE_TRANSACTION = 159;
        ........
    //处理消息
    case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                   
                    break;
}
}

找到对应的消息类型,开始处理消息,取出之前的 ClientTransaction , 并调用 其 execute 方法,继续跟进:

然后你会发现 在 ClientTransaction 类中并没有 execute 方法,因为前面我们说过把生命周期都封装成一个一个的 Item 了, 启动 ActivityLauncheActivityItem
所以我们应该去看 LaunchActivityItem

3.0 LaunchActivityItem#execute

该类继承了 ClientTransactionItem

public class LaunchActivityItem extends ClientTransactionItem {

    @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);
        client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
}

啊哈 ,好熟悉的代码,调用 handleLaunchActivity 方法,就是 ActivityThread 中的,继续走:

3.1 ActivityThread#handleLaunchActivity

@Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
     
         ...
        final Activity a = performLaunchActivity(r, customIntent);
       ....
        return a;
    }

3.2 ActivityThread#performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

      //创建ContextImpl
     ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;

        java.lang.ClassLoader cl = appContext.getClassLoader();
          //根据类名反射出要启动的Activity
         activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
          //创建Application对象
           Application app = r.packageInfo.makeApplication(false, mInstrumentation);
          //创建Window对象
          Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                appContext.setOuterContext(activity);
          //调用 attach方法
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

          //调用Activity 的 onCreate() 方法
           mInstrumentation.callActivityOnCreate(activity, r.state);
}
public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }

3.3 Activity#performCreate

final void performCreate(Bundle icicle) {
        performCreate(icicle, null);
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        mCanEnterPictureInPicture = true;
        restoreHasCurrentPermissionRequest(icicle);
        if (persistentState != null) {
            onCreate(icicle, persistentState); // 调用其 onCreate 方法
        } else {
            onCreate(icicle);
        }        
    }

终于走到头了,看到了 onCreate() 方法,由于文章篇幅过长,总结放到下一篇,路过的朋友如果对你有帮助,能否为辛苦的糖葫芦点个赞👍再走啊~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342