FrameWork层源码的分析(1)-应用程序内Activity的启动流程

主目录见:Android高级进阶知识(这是总目录索引)
 今天开始我们将渐渐进入Framework的学习,然后进而为以后的插件化做准备,要学会如何开发一套插件化框架,显然这是第一步,我们今天就来讲讲应用程序内启动Activity的流程,首先呢,我先把整个流程的图给大家看看,以便于大家学习源码的时候能有一个对照:

Activity启动

从图中可以看出,我们的中间跨进程交互都是用的AIDL的知识,如果大家不了解AIDL的话,那么我推荐看下从framework分析AIDL生成文件这篇文章或者看下更基础点的文章,大家自己决定,想必大家看完应该会知道。

一.目标

分析源码当然得有目标,我们的目标很明确:
1.了解整套Activity的启动过程;
2.为插件化框架开发做准备知识。

二.启动流程分析

我们先来明确下应用程序Activity启动的两个主要方式:

1.在Home桌面点击应用程序图标启动程序即launcher启动程序的主Activity。
2.在应用程序中调用startActivity()和startActivityForResult()来启动另外一个Activity。这个应用程序可以位于同个应用程序或者其他应用程序。

我们今天主要是讲第二种方式

public class MainActivity extends Activity  implements OnClickListener {    
    ......    
    
    @Override    
    public void onClick(View v) {    
            Intent intent = new Intent(MainActivity.this,TargetActivity.class);    
            startActivity(intent);
    }    
}    

1.因为MainActivity继承了Activity所以我们看Activity的startActivity()方法,然后经过startActivityForResult和execStartActivity最终会调用到Instrumentation的execStartActivity:

  public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
//省略掉自动测试所添加的ActivityMonitor类相关代码
......... 
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
//获取远程AMS(ActivityManagerService)
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
//检查Activity启动是否成功
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

这里我们要注意这里面传的参数,这样我们接下来要走的话不会迷失:

whoThread:Binder对象,是主进程的Context对象;
token:一个Binder对象,指向了服务端一个ActivityRecord对象;
target:目标Activity即要启动的Activity;
options:要传输的Bundle对象,里面携带数据。

2 .我们程序这里的ActivityManagerNative.getDefault()这里获取到的是ActivityManagerService的远程接口的代理即ActivityManagerProxy对象(如果不清楚可以看下AIDL的相关知识),所以这里的就是调用ActivityManagerProxy的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;
    }

这里的参数也比较多,这里的resolvedType,resultWho,profilerInfo均为null,参数caller为ApplicationThread类型的Binder实体,resultTo是为一个Binder实体的远程接口,参数requestCode为-1,startFlags为0,options为null。最后通过ActivityManagerProxy对象调用startActivity方法的实质是调用BinderProxy.transact向Binder驱动发送START_ACTIVITY_TRACSACTION命令。然后我们应用程序就到AMS中了。
3.过Binder驱动程序就进入到ActivityManagerService的startActivity方法来了,我们来看看AMS的startActivity方法:

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

4.我们看到这个方法又调用了内部的startActivityAsUser然后会调用ActivityStarter(这个类是新版的android我这里版本是android-25才有的)的一系列方法:startActivityMayWait=>startActivityLocked=>startActivityUnchecked然后会到达ActivityStackSupervisor类的resumeFocusedStackTopActivityLocked方法接着到达ActivityStack类的resumeTopActivityUncheckedLocked=>resumeTopActivityInnerLocked接着又会走到ActivityStack类的resumeTopActivityInnerLocked方法然后会先调用startPausingLocked来停掉前面一个Activity即前一个Activity要先执行onPause方法:

 final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming, boolean dontWait) {
........
   prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
........
}

5.这里的prev.app.thread是一个ApplicationThread对象的远程接口,通过调用这个远程接口的schedulePauseActivity(ApplicationThreadProxy.schedulePauseActivity)来通知前面一个Activity进入Paused状态。我们这里指的这里也是一个AIDL交互,我们不详细说明,我们直接看ActivityThread中的schedulePauseActivity方法:

  public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            int seq = getLifecycleSeq();
            if (DEBUG_ORDER) Slog.d(TAG, "pauseActivity " + ActivityThread.this
                    + " operation received seq: " + seq);
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? USER_LEAVING : 0) | (dontReport ? DONT_REPORT : 0),
                    configChanges,
                    seq);
        }

6.这里的sendMessage是发送一条消息,主要是发送到内部类H里面的handleMessage方法里面这里的消息code是PAUSE_ACTIVITY:

  case PAUSE_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    SomeArgs args = (SomeArgs) msg.obj;
                    handlePauseActivity((IBinder) args.arg1, false,
                            (args.argi1 & USER_LEAVING) != 0, args.argi2,
                            (args.argi1 & DONT_REPORT) != 0, args.argi3);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;

7.这样程序就调用了handlePauseActivity方法:

    private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport, int seq) {
        ActivityClientRecord r = mActivities.get(token);
.......... 
            // Tell the activity manager we have paused.
            if (!dontReport) {
                try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

8.我们看到程序通过AIDL( ActivityManagerNative.getDefault()获取的是AMS的远程代理)调用到AMS的activityPaused方法:

   @Override
    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

9.我们看到这里调用了ActivityStack的activityPausedLocked方法:

 final void activityPausedLocked(IBinder token, boolean timeout) {
        if (DEBUG_PAUSE) Slog.v(TAG_PAUSE,
            "Activity paused: token=" + token + ", timeout=" + timeout);

        final ActivityRecord r = isInStackLocked(token);
        if (r != null) {
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
            if (mPausingActivity == r) {
                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
                        + (timeout ? " (due to timeout)" : " (pause complete)"));
                completePauseLocked(true, null);
                return;
            } else {
            ..........
            }
        }
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }

10.我们看到程序这里会调用completePauseLocked方法来停止然后接着调用ActivityStackSupervisor类的resumeFocusedStackTopActivityLocked方法接着到达ActivityStack类的resumeTopActivityUncheckedLocked=>resumeTopActivityInnerLocked接着又会走到ActivityStack类的resumeTopActivityInnerLocked方法然后又转到ActivityStackSupervisor类的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);

        r.task.stack.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.versionCode,
                            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);
    }

11.前面首先会判断要启动的Activity的进程是否启动起来了,如果已经启动起来了会直接调用realStartActivityLocked,进行Activity的启动就可以了。但是如果我们配置了process这个属性,由于这是我们第一次启动Activity,所以进程还是没有开启的,所以我们程序会调用AMS的startProcessLocked方法:

    private final void startProcessLocked(ProcessRecord app,
            String hostingType, String hostingNameStr) {
        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
                null /* entryPoint */, null /* entryPointArgs */);
    }

我们认真注意传进来的参数分别是什么值其中entryPoint为null,然后我们直接看另一个startProcessLocked方法:

ActivityManagerService.startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs){
            ......
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
            ......
}

所以在调用上面方法的时候这里因为entryPoint传过来的是null的,所以我们的entryPoint是为android.app.ActivityThread的值,这个时候我们通过Process.start()传入的第一个参数是android.app.ActivityThread,这个参数在我们进程启动完成之后,来决定后续代码的执行 。
12.我们知道android的进程是通过zygote孵化出来的,而ActivityManagerService是运行在System进程,Zygote是运行在Zygote进程中的。Process.start方法的实现是在zygoteSendArgsAndGetResult函数最终实现的,是向socket服务端写数据,把创建进程的请求通过socket通讯方式来让zygote的管理进程创建新进程。在向zygote进程发送的通信请求时,需要组装数据,具体实现我们看Process类的start方法:

  private static ProcessStartResult startViaZygote(final String processClass,
                                  final String niceName,
                                  final int uid, final int gid,
                                  final int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            ArrayList<String> argsForZygote = new ArrayList<String>();

            // --runtime-args, --setuid=, --setgid=,
            // and --setgroups= must go first
            argsForZygote.add("--runtime-args");
            argsForZygote.add("--setuid=" + uid);
            argsForZygote.add("--setgid=" + gid);
//省略参数拼装的一些方法
.............
            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

以上的方法省略了一些拼装参数的方法,程序最终调用zygoteSendArgsAndGetResult()方法来向socket服务端发送创建进程的请求,这样zygote就会来创建新的进程。

    private static ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        try {
            // Throw early if any of the arguments are malformed. This means we can
            // avoid writing a partial response to the zygote.
            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                if (args.get(i).indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx("embedded newlines not allowed");
                }
            }

            /**
             * See com.android.internal.os.ZygoteInit.readArgumentList()
             * Presently the wire format to the zygote process is:
             * a) a count of arguments (argc, in essence)
             * b) a number of newline-separated argument strings equal to count
             *
             * After the zygote process reads these it will write the pid of
             * the child or -1 on failure, followed by boolean to
             * indicate whether a wrapper process was used.
             */
            final BufferedWriter writer = zygoteState.writer;
            final DataInputStream inputStream = zygoteState.inputStream;

            writer.write(Integer.toString(args.size()));
            writer.newLine();

            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                writer.write(arg);
                writer.newLine();
            }

            writer.flush();

            // Should there be a timeout on this?
            ProcessStartResult result = new ProcessStartResult();

            // Always read the entire result from the input stream to avoid leaving
            // bytes in the stream for future process starts to accidentally stumble
            // upon.
            result.pid = inputStream.readInt();
            result.usingWrapper = inputStream.readBoolean();

            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

13.这里就会向我们的zygote发送一个创建进程请求。然后创建新的进程就会在zygote进行,这里我们不过多去解析zygote怎么创建新的进程。最后程序会在ActivityStackSupervisor的attachApplicationLocked方法里面调用realStartActivityLocked方法:

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
            boolean andResume, boolean checkConfig) throws RemoteException {
            ......
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(task.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
            ......
            }

14.这里的app.thread是ApplicationThread,所以这里会调用ApplicationThread的scheduleLaunchActivity方法最后调用到ActivityThread的handleLaunchActivity方法:

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
   .......
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
    .......
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

15.我们看到上面方法里面会调用performLaunchActivity方法,这个方法就是真正启动Activity了,我们看下这个具体方法:

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
......
        Activity activity = null;
        try {
//这里调用Instrumentation的newActivity来创建一个新的Activity
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
......      
        } catch (Exception e) {
......
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

........
                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);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                if (r.isPersistable()) {
//这个方法里面会调用目标Activity的onCreate方法
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                        " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
.......
        } catch (SuperNotCalledException e) {
            throw e;

        } catch (Exception e) {
......
        }

        return activity;
    }

16.我们看到这个方法就是真正创建Activity的地方了,首先会调用Instrumentation的newActivity方法:

  public Activity newActivity(ClassLoader cl, String className,
            Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        return (Activity)cl.loadClass(className).newInstance();
    }

17.这里很容易看懂就是用类加载器加载一个新的class,然后返回他的实例。到这一步Activity的实例也就被创建出来了,接着我们看会调用Activity的onCreate方法,这个会在Instrumentation的callActivityOnCreate方法里面:

 public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

我们看到这个方法里面会调用activity的performCreate方法,这个方法如下:

    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

到这里我们的Activity就启动完毕了,如果你中间有什么过程不懂的可以参照我们前面画的图来对照,基本流程就知道了,也不至于被绕晕。

总结:这次的Activity启动讲解主要是让大家有个流程上的认识,到时如果在插件化框架上有用到我会认真把某一个地方拿出来讲解,如果有讲解的不对的地方欢迎大家指出哈,希望大家共同进步。

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

推荐阅读更多精彩内容