SystemUI源码分析

这里分析的是Android 9的源代码

启动篇(1)

SystemServer.java

1.执行main函数,开始跑线程

    public static void main(String[] args) {
        new SystemServer().run();
    }

2.执行startOtherServices函数

private void run() {
        // Start services.
        try {
             traceBeginAndSlog("StartServices");
             startBootstrapServices();
             startCoreServices();
             startOtherServices();
        } catch (Throwable ex) {
            throw ex;
        } finally {
            traceEnd();
        }
    }

3.执行startSystemUi函数

private void startOtherServices(){
        traceBeginAndSlog("StartSystemUI");
        try {
              startSystemUi(context, windowManagerF);
            }
        catch (Throwable e) {
              reportWtf("starting System UI", e);
             }
        traceEnd();
 }

4.开始拉起systemUIService服务,并且WindowManagerService也调用onSystemUiStarted方法

  static final void startSystemUi(Context context, WindowManagerService windowManager) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        context.startServiceAsUser(intent, UserHandle.SYSTEM);
        windowManager.onSystemUiStarted();
    }

WindowManagerService.java

5.这里面的 windowManager实际上是PhoneWindowManager,

    public void onSystemUiStarted() {
        mPolicy.onSystemUiStarted();
    }

KeyguardServiceDelegate.java

6.最终跟到源码里面,它做的事情就是PhoneWindowManager与锁屏服务做了bindService的操作,这块我们就不再跟下去了,接下来我们主要去看看SystemUIService都做了什么操作

  public void bindService(Context context) {
        Intent intent = new Intent();
        final Resources resources = context.getApplicationContext().getResources();

        final ComponentName keyguardComponent = ComponentName.unflattenFromString(
                resources.getString(com.android.internal.R.string.config_keyguardComponent));
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        intent.setComponent(keyguardComponent);
        //对应的是锁屏服务,com.android.systemui.keyguard.KeyguardService
        if (!context.bindServiceAsUser(intent, mKeyguardConnection,
                Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
            Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);

        } else {
            if (DEBUG) Log.v(TAG, "*** Keyguard started");
        }
    }

SystemUIService.java

7.SystemUI是一个app,是一个persistent 应用

SystemUIService它直接调用的是SystemUIApplication的startServicesIfNeeded

public void onCreate() {
    ((SystemUIApplication) getApplication()).startServicesIfNeeded();
}

SystemUIApplication.java

8.SystemUIApplication的startServicesIfNeeded函数

public void startServicesIfNeeded() {
    String[] names =getResources().getStringArray(R.array.config_systemUIServiceComponents);
    startServicesIfNeeded(names);
}

9.启动以下系统组件

        <item>com.android.systemui.Dependency</item>
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.stackdivider.Divider</item>
        <item>com.android.systemui.SystemBars</item>
        <item>com.android.systemui.usb.StorageNotification</item>
        <item>com.android.systemui.power.PowerUI</item>
        <item>com.android.systemui.media.RingtonePlayer</item>
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.pip.PipUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>

UML类图(systemui组件)

image.png

10.启动上面的systemui的组件,它们都是继承于systemui的类,其组件架构如下图

private void startServicesIfNeeded(String[] services) {
        if (mServicesStarted) {
            return;
        }
        //构建系统组件列表
        mServices = new SystemUI[services.length];

        final int N = services.length;
        for (int i = 0; i < N; i++) {
            String clsName = services[i];
            Class cls;
            try {
                //通过反射拿到组件类
                cls = Class.forName(clsName);
                mServices[i] = (SystemUI) cls.newInstance();
            } catch(ClassNotFoundException ex){
                throw new RuntimeException(ex);
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            }
            mServices[i].mContext = this;
            mServices[i].mComponents = mComponents;
            //调用组件的start方法,如果组件启动超过1秒,会有打印
            mServices[i].start();
            ti = System.currentTimeMillis() - ti;
            if (ti > 1000) {
                Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
            }
                                //如果系统重启成功,调用组件的onBootCompleted方法
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }

        //获取PluginManagerImpl对象,并添加插件监听
        Dependency.get(PluginManager.class).addPluginListener(
                new PluginListener<OverlayPlugin>() {
                    private ArraySet<OverlayPlugin> mOverlays;

                    @Override
                    public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
                        StatusBar statusBar = getComponent(StatusBar.class);
                        if (statusBar != null) {
                            plugin.setup(statusBar.getStatusBarWindow(),
                                    statusBar.getNavigationBarView());
                        }
                        // Lazy init.
                        if (mOverlays == null) mOverlays = new ArraySet<>();
                        if (plugin.holdStatusBarOpen()) {
                            mOverlays.add(plugin);
                            Dependency.get(StatusBarWindowManager.class).setStateListener(b ->
                                    mOverlays.forEach(o -> o.setCollapseDesired(b)));
                            Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                                    mOverlays.size() != 0);

                        }
                    }

                    @Override
                    public void onPluginDisconnected(OverlayPlugin plugin) {
                        mOverlays.remove(plugin);
                        Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
                                mOverlays.size() != 0);
                    }
                }, OverlayPlugin.class, true /* Allow multiple plugins */);

        mServicesStarted = true;
    }

总结:从代码流程来看,其实相对来说还是很简单的。SystemServer启动后,会启动SystemUI服务,启动SystemSUI服务的同时,锁屏相关的服务也会拉起,而SystemUI启动的核心内容其实也就是各组件启动的过程。下面我将带大家看看其中的VolumeUI组件,也就是大家熟悉的音量条组件。

组件篇Dependency(2)

Dependency.java

1.书接上文,我们知道SystemUI应用里面有很多继承于SystemUI类的组件,其中Dependency就是第一个组件

public class Dependency extends SystemUI {
    //依赖对象的映射缓存列表
    private final ArrayMap<Object, Object> mDependencies = new ArrayMap<>();
    //cls<->数据提供者的映射列表
    private final ArrayMap<Object, DependencyProvider> mProviders = new ArrayMap<>();
}

2.执行start函数,先将Controller和ControllerImpl的映射关系放到mProviders中,后续get函数要用mProviders的数据


public void start() {
        //初使化数据
        mProviders.put(PluginManager.class, () -> new PluginManagerImpl(mContext));       
        mProviders.put(StatusBarIconController.class, () -> new StatusBarIconControllerImpl(mContext));
        ...
        mProviders.put(VolumeDialogController.class, () -> new VolumeDialogControllerImpl(mContext));
        ...           
        //将所有依赖项放在上面,这样工厂就可以根据需要覆盖它们。
        SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
        sDependency = this;      
  }

3.get函数,实际上是从拿到的也就是mProviders中put的xxxControllerImpl。这里的mDependencies充当了缓存的作用,只有当用到的时候才会实现xxxControllerImpl,并放到内存中

    public static <T> T get(Class<T> cls) {
        return sDependency.getDependency(cls);
    }

    protected final <T> T getDependency(Class<T> cls) {
        return getDependencyInner(cls);
    }

    private synchronized <T> T getDependencyInner(Object key) {
        @SuppressWarnings("unchecked")
        T obj = (T) mDependencies.get(key);
        if (obj == null) {
            //如果没有mDependencies,就创建一个
            obj = createDependency(key);
            mDependencies.put(key, obj);
        }
        return obj;
    }

4.createDependency函数,实际上拿到的就是上面的xxxControllerImpl.

    @VisibleForTesting
    protected <T> T createDependency(Object cls) {
        Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);

        @SuppressWarnings("unchecked")
        //拿到数据的提供者
        DependencyProvider<T> provider = mProviders.get(cls);
        if (provider == null) {
            throw new IllegalArgumentException("Unsupported dependency " + cls
                    + ". " + mProviders.size() + " providers known.");
        }
        //通过数据提供者创建依赖关系
        return provider.createDependency();
    }

5.通知mDependencies缓存中各继承了ConfigurationChangedReceiver接口组件的xxxControllerImpl,并回调它们的onConfigurationChanged

    @Override
    protected synchronized void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDependencies.values().stream().filter(obj -> obj instanceof ConfigurationChangedReceiver)
                .forEach(o -> ((ConfigurationChangedReceiver) o).onConfigurationChanged(newConfig));
    }

总结:其实Dependency就是维护了一个数据对象数据集而已,方便统一调用共有的函数,也方便其他的类做一个对象引用,

组件篇VolumeUI(3)

VolumeUI.java

1.书接上文,我们知道SystemUI应用里面有很多继承于SystemUI类的组件,其中VolumeUI就是之一

public class VolumeUI extends SystemUI

2.执行start函数

    public void start() {
        //先判断VolumeUI能不能用
        boolean enableVolumeUi = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
        boolean enableSafetyWarning =
            mContext.getResources().getBoolean(R.bool.enable_safety_warning);
        mEnabled = enableVolumeUi || enableSafetyWarning;
        if (!mEnabled) return;

        //VolumeUI 绑定实际的组件 VolumeDialogComponent
        mVolumeComponent = new VolumeDialogComponent(this, mContext, null);
        mVolumeComponent.setEnableDialogs(enableVolumeUi, enableSafetyWarning);
        //添加映射关系到组件列表
        putComponent(VolumeComponent.class, mVolumeComponent);
        //组件注册
        mVolumeComponent.register();
    }

VolumeDialogComponent.java

3.我们来看看VolumeDialogComponent的构造函数

    public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) {
        mSysui = sysui;
        mContext = context;
        //这里我们可以看到实际引用是VolumeDialogControllerImpl
        mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class);
        //如果发生了用户活动,就会通知电源管理器PowerManager
        mController.setUserActivityListener(this);

        //允许插件引用VolumeDialogController

        //PluginDependencyProvider-->PluginDependencyProvider-->PluginManagerImpl
        //将VolumeDialogControllerImpl放到PluginDependencyProvider的mDependencies中
        Dependency.get(PluginDependencyProvider.class)
                .allowPluginDependency(VolumeDialogController.class);

        //初始化一个扩展工具,用于VolumeDialogImpl(View)的初使化,   
        Dependency.get(ExtensionController.class)
                .newExtension(VolumeDialog.class)
                .withPlugin(VolumeDialog.class)
                .withDefault(this::createDefault)//createDefault()方法返回了一个VolumeDialogImpl实例化对象
                .withFeature(PackageManager.FEATURE_AUTOMOTIVE, this::createCarDefault)
                .withCallback(dialog -> {
                    if (mDialog != null) {
                        mDialog.destroy();
                    }
                    mDialog = dialog;
                    //基实VolumeDialogImpl就是在这个地方完成了初使化的回调
                    mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback);
                }).build();
        applyConfiguration();
        //注册Observer监听,并回调自己的onTuningChanged(),更新mVolumePolicy
        Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT,
                VOLUME_SILENT_DO_NOT_DISTURB);
    }

    //start函数会固定执行register
    @Override
    public void register() {
        mController.register();
        DndTile.setCombinedIcon(mContext, true);
    }

    //VolumeDialog的具体实现是VolumeDialogImpl
    private VolumeDialog createDefault() {
        VolumeDialogImpl impl = new VolumeDialogImpl(mContext);
        impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
        impl.setAutomute(true);
        impl.setSilentMode(false);
        return impl;
    }

VolumeDialogControllerImpl.java(Presenter层)

4.我们来看看VolumeDialogControllerImpl的构造函数

    protected final VC mVolumeController = new VC();

    public VolumeDialogControllerImpl(Context context) {
        mContext = context.getApplicationContext();
        mNotificationManager = (NotificationManager) mContext.getSystemService(
                Context.NOTIFICATION_SERVICE);
        //打印事件log
        Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED);
        //子线程的handler 
        mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName());
        mWorkerThread.start();
        mWorker = new W(mWorkerThread.getLooper());
        //获取mMediaSessions 
        mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(),
                mMediaSessionsCallbacksW);
       //获取AudioManager
        mAudio = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        mNoMan = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        mObserver = new SettingObserver(mWorker);
        mObserver.init();
        mReceiver.init();
         //获取震动对象
        mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
        mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
        //获取AudioService 
        mAudioService = IAudioService.Stub.asInterface(
                ServiceManager.getService(Context.AUDIO_SERVICE));
        //更新statusBar
        updateStatusBar();
        //更新声音控制的策略
        boolean accessibilityVolumeStreamActive = context.getSystemService(
                AccessibilityManager.class).isAccessibilityVolumeStreamActive();
        //这时的mVolumeController是
        mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
                    VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
                        VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
    }

5.再看看他的register

  //
  public void register() {
        //设置音量控制器
        setVolumeController();
        setVolumePolicy(mVolumePolicy);
        showDndTile(mShowDndTile);
        try {
            mMediaSessions.init();
        } catch (SecurityException e) {
            Log.w(TAG, "No access to media sessions", e);
        }
    }

   //将AIDL的control对象通过AudioManager传给AudioService内部,这样就实现了与AudioService的联动
   protected void setVolumeController() {
        try {
            mAudio.setVolumeController(mVolumeController);
        } catch (SecurityException e) {
            Log.w(TAG, "Unable to set the volume controller", e);
            return;
        }
    } 

    //AIDL对象
   private final class VC extends IVolumeController.Stub {
         //AudioService的一些内部回调,会回到这里,再通过mWorker(handler)对象传给本线程来处理
        @Override
        public void volumeChanged(int streamType, int flags) throws RemoteException {
            mWorker.obtainMessage(W.VOLUME_CHANGED, streamType, flags).sendToTarget();
        }
   }

  //当 AudioService内部触发了onVolumeChanged时,就会通过mWorker回调给本线程的onVolumeChangedW来处理
  boolean onVolumeChangedW(int stream, int flags) {
        final boolean showUI = shouldShowUI(flags);
        final boolean fromKey = (flags & AudioManager.FLAG_FROM_KEY) != 0;
        final boolean showVibrateHint = (flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0;
        final boolean showSilentHint = (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0;
        boolean changed = false;
        if (showUI) {
            changed |= updateActiveStreamW(stream);
        }
        int lastAudibleStreamVolume = getAudioManagerStreamVolume(stream);
        changed |= updateStreamLevelW(stream, lastAudibleStreamVolume);
        changed |= checkRoutedToBluetoothW(showUI ? AudioManager.STREAM_MUSIC : stream);
        if (changed) {
            mCallbacks.onStateChanged(mState);
        }
        if (showUI) {
            //如果要显示UI的话,就会通过mCallbacks回调出去,onShowRequested
            mCallbacks.onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
        }
        if (showVibrateHint) {
            mCallbacks.onShowVibrateHint();
        }
        if (showSilentHint) {
            mCallbacks.onShowSilentHint();
        }
        if (changed && fromKey) {
            Events.writeEvent(mContext, Events.EVENT_KEY, stream, lastAudibleStreamVolume);
        }
        return changed;
    }

6.那Callback从哪里来的呢,可以看到callback是内部的监听器,外部的想要监听变化的对象,都可以通过addCallback函数添加到内部管理的hashmap里面

     protected C mCallbacks = new C();

     public void addCallback(Callbacks callback, Handler handler) {
        mCallbacks.add(callback, handler);
        callback.onAccessibilityModeChanged(mShowA11yStream);
    }

    class C implements Callbacks {
        private final HashMap<Callbacks, Handler> mCallbackMap = new HashMap<>();

        public void add(Callbacks callback, Handler handler) {
            if (callback == null || handler == null) throw new IllegalArgumentException();
            mCallbackMap.put(callback, handler);
        }

        public void remove(Callbacks callback) {
            mCallbackMap.remove(callback);
        }

        @Override
        public void onShowRequested(final int reason) {
            for (final Map.Entry<Callbacks, Handler> entry : mCallbackMap.entrySet()) {
                entry.getValue().post(new Runnable() {
                    @Override
                    public void run() {
                        entry.getKey().onShowRequested(reason);
                    }
                });
            }
        }
    }

AudioManager.java(Model层)

    public void setVolumeController(IVolumeController controller) {
        try {
            getService().setVolumeController(controller);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

VolumeDialogImpl.java(View层)

7.那我们来看看都有谁addCallback了,其中有就VolumeDialogImpl.java

    public VolumeDialogImpl(Context context) {
        //通过Dependency拿到VolumeDialogControllerImpl对象
        mController = Dependency.get(VolumeDialogController.class);
    }

    public void init(int windowType, Callback callback) {
        initDialog();
        mAccessibility.init();
        //往VolumeDialogControllerImpl对象里面添加mControllerCallbackH的监听
        mController.addCallback(mControllerCallbackH, mHandler);
        mController.getState();
    }

    private final VolumeDialogController.Callbacks mControllerCallbackH
            = new VolumeDialogController.Callbacks() {
        @Override
        public void onShowRequested(int reason) {
            //收到VolumeDialogControllerImpl的onShowRequested回调以后,就调用show函数
            showH(reason);
        }
    };

    private void showH(int reason) {
        if (D.BUG) Log.d(TAG, "showH r=" + Events.DISMISS_REASONS[reason]);
        mHandler.removeMessages(H.SHOW);
        mHandler.removeMessages(H.DISMISS);
        rescheduleTimeoutH();
        mShowing = true;

        initSettingsH();
        //最终在这里进行了dialog的展示
        mDialog.show();
        Events.writeEvent(mContext, Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
        mController.notifyVisible(true);
    }

UML类图(VolumeUI)

VolumeUI类图.png

总结:到了这里,我们大概可以掌握systemui的启动,以及systemui内部的组件是怎么关联调用的。

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

推荐阅读更多精彩内容