Android面试官装逼失败之:Activity的启动模式

面试的时候,面试官经常同你随便侃侃Activity的启动模式,但Activity启动牵扯的知识点其实很多,并非能单单用四个启动模式就能概括的,默认的启动模式的表现会随着Intent Flag的设置而改变,因此侃Activity启动模式大多走流程装逼,最多结合项目遇到的问题,随便刁难一下面试者,并不太容易把控,也许最后,面试官跟面试者的答案都是错了,比如在Service中必须通过设置FLAG_ACTIVITY_NEW_TASK才能启动Activity,这个时候启动Activit会有什么样的表现呢?就这一个问题,答案就要分好几个场景:

  • Activity的taskAffinity属性的Task栈是否存在
  • 如果存在,要看Activity是否存已经存在于该Task
  • 如果已经存在于该taskAffinity的Task,要看其是不是其rootActivity
  • 如果是其rootActivity,还要看启动该Activity的Intent是否跟当前intent相等

不同场景,所表现的行为都会有所不同,再比如singleInstance属性,如果设置了,大家都知道只有一个实例,将来再启动会复用,但是如果使用Intent.FLAG_ACTIVITY_CLEAR_TASK来启动,仍然会重建,并非完全遵守singleInstance的说明,还有不同Flag在叠加使用时候也会有不同的表现,单一而论Activity启动模式其实是很难的。本文也仅仅是涉及部分启动模式及Flag,更多组合跟场景要自己看源码或者实验来解决了。

简单基本launchmode

本文假定大家对于Activity的Task栈已经有初步了解,首先,看一下Activity常见的四种启动模式及大众理解,这也是面试时最长问的:

  • standard:标准启动模式(默认启动模式),每次都会启动一个新的activity实例。
  • singleTop:单独使用使用这种模式时,如果Activity实例位于当前任务栈顶,就重用栈顶实例,而不新建,并回调该实例onNewIntent()方法,否则走新建流程。
  • singleTask:这种模式启动的Activity只会存在相应的Activity的taskAffinit任务栈中,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,会重新唤起该实例,并清理当前Task任务栈该实例之上的所有Activity,同时回调onNewIntent()方法。
  • singleInstance:这种模式启动的Activity独自占用一个Task任务栈,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,只会唤起原实例,并回调onNewIntent()方法。

需要说明的是:上面的场景仅仅适用于Activity启动Activity,并且采用的都是默认Intent,没有额外添加任何Flag,否则表现就可能跟上面的完全不一致,尤其要注意的是FLAG_ACTIVITY_NEW_TASK的使用,后面从源码中看,依靠FLAG_ACTIVITY_NEW_TASK其实可以分为两派。

Intent.FLAG_ACTIVITY_NEW_TASK分析

从源码来看,Intent.FLAG_ACTIVITY_NEW_TASK是启动模式中最关键的一个Flag,依据该Flag启动模式可以分成两类,设置了该属性的与未设置该属性的,对于非Activity启动的Activity(比如Service或者通知中启动的Activity)需要显示的设置Intent.FLAG_ACTIVITY_NEW_TASK,而singleTask及singleInstance在AMS中被预处理后,隐形的设置了Intent.FLAG_ACTIVITY_NEW_TASK,而启动模式是standard及singletTop的Activity不会被设置Intent.FLAG_ACTIVITY_NEW_TASK,除非通过显示的intent setFlag进行设置。

FLAG_ACTIVITY_NEW_TASK这个属性更多的关注点是在Task,可以认为没有设置FLAG_ACTIVITY_NEW_TASK的情况下,taskAffinity可以不考虑,大多数情况下,需要将Activity引入到自己taskAffinity的Task中,Intent.FLAG_ACTIVITY_NEW_TASK的初衷是在Activity目标taskAffinity的Task中启动,非Activity启动Activity都必须添加Intent.FLAG_ACTIVITY_NEW_TASK才行,以Service启动的Activity为例:

  Intent intent = new Intent(BackGroundService.this, A.class);
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  startActivity(intent);    

这种情况很有意思,如果目标Activity实例或者Task不存在,则一定会新建Activity,并将目标Task移动到前台,但是如果Activity存在,却并不一定复用,也不一定可见。这里假定A是standard的Activity,如果已经有一个A实例,并且所在的堆栈的taskAffinity跟A的taskAffinity一致,这个时候要看这个task的根Activity是不是A,如果是A,还要看A的intent是不是跟当前的启动的intent相等,如果都满足,只要将task可见即可。否则,就需要新建A,并根据A的task栈的存在情况而选择直接入栈还是新建栈。但是,如果Intent想要的启动的Activity的目标堆栈存在,那就将整个堆栈往前迁移,如果位于顶部的Task栈正好是目标Activity的Task栈,那就不做任何处理,连onNewIntent都不会回调,怎么判断目标的Activity的Task栈同找到的栈一致呢?如果找不到目标Task自然会启动Task,如果目标task栈根Activit的intent同新将要启动的Activit相同,就不启动新Activity,否则启动Activity

Service 通过 FLAG_ACTIVITY_NEW_TASK.jpg

Intent.FLAG_ACTIVITY_CLEAR_TASK:必须配合FLAG_ACTIVITY_NEW_TASK使用

If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.

这个属性必须同FLAG_ACTIVITY_NEW_TASK配合使用,如果设置了FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK,如果目标task已经存在,将清空已存在的目标Task,否则,新建一个Task栈,之后,新建一个Activity作为根Activity。Intent.FLAG_ACTIVITY_CLEAR_TASK的优先级最高,基本可以无视所有的配置,包括启动模式及Intent Flag,哪怕是singleInstance也会被finish,并重建。

Intent.FLAG_ACTIVITY_CLEAR_TASK.jpg

Intent.FLAG_ACTIVITY_CLEAR_TOP

如果没有使用FLAG_ACTIVITY_NEW_TASK,目标是当前Task栈,根据不同的组合会产生不同的效果,如果单独使用Intent.FLAG_ACTIVITY_CLEAR_TOP,并且没有设置特殊的launchmode,那么,Google官方的示例是:如果ABCD Task中的D采用Intent.FLAG_ACTIVITY_CLEAR_TOP唤起B,这个时候首先会将CD出栈,但是至于B是否会重建,要视情况而定,如果没有设置FLAG_ACTIVITY_SINGLE_TOP,则会将B finish掉,之后创建新的入栈。如果同一个栈中原来有

Intent.FLAG_ACTIVITY_CLEAR_TOP同一个.jpg

如果没有则新建,不会去另一个栈中寻找。

Intent.FLAG_ACTIVITY_CLEAR_TOP.jpg

如果同时设置了FLAG_ACTIVITY_SINGLE_TOP,在当前栈已有的情况下就不会重建,而是直接回调B的onNewIntent(),

Intent.FLAG_ACTIVITY_CLEAR_TOP|SINGLE_TOP.jpg

官方解释如下:

For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B。

The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent. If it has declared its launch mode to be "multiple" (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance's onNewIntent().

如果同时使用了FLAG_ACTIVITY_NEW_TASK ,这个时候,目标是Activity自己所属的Task栈,如果在自己的Task中能找到一个Activity实例,则将其上面的及自身清理掉,之后重建。

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK.jpg

如果同时在加上FLAG_ACTIVITY_SINGLE_TOP,会更特殊一些,如果topActivity不是目标Activity,就会去目标Task中去找,并唤起

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop.jpg

如果topActivity是目标Activity,就直接回调topActivity的onNewIntent,无论topActivity是不是在目标Task中

Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop|top.jpg

Intent.FLAG_ACTIVITY_SINGLE_TOP

Intent.FLAG_ACTIVITY_SINGLE_TOP多用来做辅助作用,跟launchmode中的singleTop作用一样,在Task栈顶有的话,就不新建,栈顶没有的话,就新建,这里的Task可能是目标栈,也可能是当前Task栈,配合FLAG_ACTIVITY_NEW_TASK及FLAG_ACTIVITY_CLEAR_TOP都会有很有意思的效果。

源码分析

现在我们看一下源码,来分析下原理:从源码更能看出FLAG_ACTIVITY_NEW_TASK重要性,这里只分析部分场景,不然太多了: 下面的是4.3的代码,比较简单些:

final int startActivityUncheckedLocked(ActivityRecord r,
        ActivityRecord sourceRecord, int startFlags, boolean doResume,
        Bundle options) {
    final Intent intent = r.intent;
    final int callingUid = r.launchedFromUid;
    ...
    <!--关键点1:直接获取launchFlags--> 
    int launchFlags = intent.getFlags();
    ...
     <!--关键点2:预处理一些特殊的 launchmode,主要是设置Intent.FLAG_ACTIVITY_NEW_TASK-->
    // 如果sourceRecord ==null 说明不是从activity启动的,从服务开启就一定要start a new task
    if (sourceRecord == null) {
        if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
        }
    } else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
        launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    } else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
        launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
    }
    ...
    <!--关键点3 对于Intent.FLAG_ACTIVITY_NEW_TASK 对sourceActivity直接返回cancel-->
    if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        sendActivityResultLocked(-1,
                r.resultTo, r.resultWho, r.requestCode,
            Activity.RESULT_CANCELED, null);
        r.resultTo = null;
    }
    boolean addingToTask = false;
    boolean movedHome = false;
    TaskRecord reuseTask = null;
     <!--关键点4 对于Intent.FLAG_ACTIVITY_NEW_TASK分支的处理-->
    if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
            (launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
        if (r.resultTo == null) {
            <!--关键点5找到目标Task栈的栈顶元素 ,但是taskTop不一定是目标Activity-->   
            ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
                    ?findTaskLocked(intent, r.info)
                    : findActivityLocked(intent, r.info);
            <!--如果目标栈存在-->
            if (taskTop != null) {
                ActivityRecord curTop = topRunningNonDelayedActivityLocked(notTop);
                <!--关键点6 看找到的Task是不是位于栈顶,如果不是则移动-->
                if (curTop != null && curTop.task != taskTop.task) {
                    r.intent.addFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
                    boolean callerAtFront = sourceRecord == null
                            || curTop.task == sourceRecord.task;
                    if (callerAtFront) {
                        movedHome = true;
                        moveHomeToFrontFromLaunchLocked(launchFlags);
                        moveTaskToFrontLocked(taskTop.task, r, options);
                        options = null;
                    }
                }
                    ...
                    <!--关键点6 如果设置了Intent.FLAG_ACTIVITY_CLEAR_TASK,则重置Task,并为reuseTask赋值,这个属性只能配合Intent.FLAG_ACTIVITY_NEW_TASK使用-->
                if ((launchFlags &
                        (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK))
                        == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK)) {
                    reuseTask = taskTop.task;
                    performClearTaskLocked(taskTop.task.taskId);
                    reuseTask.setIntent(r.intent, r.info);
                 <!--关键点7 如果设置了Intent.FLAG_ACTIVITY_CLEAR_TOP,则将Task中目标Activity之上的清空,至于自己是否要清空,还要看是不是设置了singltTop-->
                } else if ((launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
                    <!--如果能找到一个SingleTop,则不用重建-->
                    ActivityRecord top = performClearTaskLocked(
                            taskTop.task.taskId, r, launchFlags);
                    if (top != null) {
                    <!--如果自己是rootActivity,则重置intent-->
                        if (top.frontOfTask) {
                            top.task.setIntent(r.intent, r.info);
                        }
                        top.deliverNewIntentLocked(callingUid, r.intent);
                    } else {
                     <!--找不到的话,就新建 ,并且为sourceRecord赋值-->
                        addingToTask = true;
                        sourceRecord = taskTop;
                    }
                } else if (r.realActivity.equals(taskTop.task.realActivity)) {
                        <!--如果taskTop的Task rootActivity是目标Activity,则额外,如果是Intent.FLAG_ACTIVITY_SINGLE_TOP,并自己位于栈顶 不新建-->
                    if (((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                            || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP)
                            && taskTop.realActivity.equals(r.realActivity)) {
                        if (taskTop.frontOfTask) {
                            taskTop.task.setIntent(r.intent, r.info);
                        }
                        taskTop.deliverNewIntentLocked(callingUid, r.intent);
                        <!--如果root intent不相等,则新建Activity,并加入目标栈-->
                    } else if (!r.intent.filterEquals(taskTop.task.intent)) {
                        addingToTask = true;
                        sourceRecord = taskTop;
                    }
                }
                 ...
                 <!--以上部分主要是对Intent.FLAG_ACTIVITY_NEW_TASK的处理,有些需要直接返回的场景-->
                if (!addingToTask && reuseTask == null) {
                    if (doResume) {
                        resumeTopActivityLocked(null, options);
                    } else {
                        ActivityOptions.abort(options);
                    }
                    return ActivityManager.START_TASK_TO_FRONT;
                }
            }
        }
    }
    
    if (r.packageName != null) {
        ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
        if (top != null && r.resultTo == null) {
            if (top.realActivity.equals(r.realActivity) && top.userId == r.userId) {
                if (top.app != null && top.app.thread != null) {
               <!--可能不是目标task,但是是Intent.FLAG_ACTIVITY_SINGLE_TOP-->
                    if ((launchFlags&Intent.FLAG_ACTIVITY_SINGLE_TOP) != 0
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP
                        || r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
                        logStartActivity(EventLogTags.AM_NEW_INTENT, top, top.task);
                        if (doResume) {
                            resumeTopActivityLocked(null);
                        }
                        ActivityOptions.abort(options);
                        if ((startFlags&ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
                            return ActivityManager.START_RETURN_INTENT_TO_CALLER;
                        }
                        top.deliverNewIntentLocked(callingUid, r.intent);
                        return ActivityManager.START_DELIVERED_TO_TOP;
                    }
                }
            }
        }

    }
    ...

    boolean newTask = false;
    boolean keepCurTransition = false;

    <!--最终判断是不是要启动新Task栈-->
    if (r.resultTo == null && !addingToTask
            && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
        <!--新建Task 没找到reuseTask task 并且是launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK-->
        if (reuseTask == null) {
            mService.mCurTask++;
            if (mService.mCurTask <= 0) {
                mService.mCurTask = 1;
            }
            r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
        } else {
        <!--找到Task,但是rootActivity不是目标Activiyt,则需要新建activity-->
            r.setTask(reuseTask, reuseTask, true);
        }
        newTask = true;
        if (!movedHome) {
            moveHomeToFrontFromLaunchLocked(launchFlags);
        }
        
    } else if (sourceRecord != null) {
            <!--非服务类启动的Activity,如果当前栈能找到目标栈的singleTop,则不新建,否则新建-->
        if (!addingToTask && (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
            ActivityRecord top = performClearTaskLocked(
                    sourceRecord.task.taskId, r, launchFlags);
            keepCurTransition = true;
            if (top != null) {
                top.deliverNewIntentLocked(callingUid, r.intent);
                if (doResume) {
                    resumeTopActivityLocked(null);
                }
                ActivityOptions.abort(options);
                return ActivityManager.START_DELIVERED_TO_TOP;
            }
        } 
        ...
        r.setTask(sourceRecord.task, sourceRecord.thumbHolder, false);
    } else {
        final int N = mHistory.size();
        ActivityRecord prev =
            N > 0 ? mHistory.get(N-1) : null;
        r.setTask(prev != null
                ? prev.task
                : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
   }
    startActivityLocked(r, newTask, doResume, keepCurTransition, options);
    return ActivityManager.START_SUCCESS;
}
Activity启动模式.jpg

为什么非Activity启动Activity要强制规定使用参数FLAG_ACTIVITY_NEW_TASK

从源码上说,ContextImpl在前期做了检查,如果没添加Intent.FLAG_ACTIVITY_NEW_TASK就抛出异常,

@Override
public void startActivity(Intent intent, Bundle options) {
    warnIfCallingFromSystemProcess();
    if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
        throw new AndroidRuntimeException(
                "Calling startActivity() from outside of an Activity "
                + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                + " Is this really what you want?");
    }
    ...
}

为什么要这么呢?其实直观很好理解,如果不是在Activity中启动的,那就可以看做不是用户主动的行为,也就说这个界面可能出现在任何APP之上,如果不用Intent.FLAG_ACTIVITY_NEW_TASK将其限制在自己的Task中,那用户可能会认为该Activity是当前可见APP的页面,这是不合理的。举个例子:我们在听音乐,这个时候如果邮件Service突然要打开一个Activity,如果不用Intent.FLAG_ACTIVITY_NEW_TASK做限制,那用户可能认为这个Activity是属于音乐APP的,因为用户点击返回的时候,可能会回到音乐,而不是邮件(如果邮件之前就有界面)。

总结

以上分析只是针对一个版本的Android,并且只涉及部分Flag,要完全理解各种组合就更麻烦了,所以所,如果面试官问题Activity启动模式的话,随便侃侃还可以,但是要以此来鄙视你,那你有90%的概率可以怼回去,怎么怼?随便几个Flag组合一下,问面试官会有什么结果,保证问死一堆 _

让你装逼

作者:看书的小蜗牛
原文链接: Android面试官装逼失败之:Activity的启动模式
仅供参考,欢迎指正

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

推荐阅读更多精彩内容