SurfaceView源碼分析(一):Surface的創(chuàng)建過(guò)程

前言

一個(gè)窗口里面的所有的普通View都會(huì)共享一個(gè)Surface,這個(gè)Surface保存在ViewRootImpl里面捆等。但是SurfaceView不同降铸,SurfaceView自己獨(dú)享一個(gè)Surface住拭,也就意味著每次刷新SurfaceViewUI的時(shí)候潭袱,可以不必通知ViewRootImpl柱嫌,這樣會(huì)大大提高繪制效率。


當(dāng)然SurfaceView本身也是繼承View的屯换,都在一個(gè)View Hierarchy中编丘,所以自然也會(huì)遵循View的繪制流程。不過(guò)SurfaceView自身調(diào)用了setWillNotDraw(true)方法彤悔,所以本身是不會(huì)調(diào)用onDraw的嘉抓。接下來(lái)我們就開始往下探究SurfaceView的Surface到底是怎么創(chuàng)建的:

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    private void performTraversals() {
        // cache mView since it is used so much below...
       final View host = mView;
       ...代碼省略...
       if (viewVisibilityChanged) {
            mAttachInfo.mWindowVisibility = viewVisibility;
            host.dispatchWindowVisibilityChanged(viewVisibility);
            if (viewUserVisibilityChanged) {
                host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE);
            }
            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
                endDragResizing();
                destroyHardwareResources();
            }
            if (viewVisibility == View.GONE) {
                // After making a window gone, we will count it as being
                // shown for the first time the next time it gets focus.
                mHasHadWindowFocus = false;
            }
        }
       ...代碼省略...
    }
}

ViewRootImpl#performTraversals這個(gè)方法應(yīng)該都比較熟悉了,每次View請(qǐng)求重新測(cè)量或者繪制的時(shí)候都會(huì)執(zhí)行這個(gè)方法蜗巧,另外View剛被添加到ViewTree的時(shí)候也會(huì)執(zhí)行一次。
這里當(dāng)當(dāng)前窗口的可見性發(fā)生了變化蕾盯,就會(huì)執(zhí)行host.dispatchWindowVisibilityChanged方法幕屹,這里的host是ViewRootImpl 的mView成員變量,也就是一個(gè)窗口中最頂層的View——DecorView级遭,DecorView繼承了ViewGroup望拖,自身并沒有重寫dispatchWindowVisibilityChanged方法,所以進(jìn)入ViewGroup看下:

    @Override
    public void dispatchWindowVisibilityChanged(int visibility) {
        super.dispatchWindowVisibilityChanged(visibility);
        final int count = mChildrenCount;
        final View[] children = mChildren;
        for (int i = 0; i < count; i++) {
            children[i].dispatchWindowVisibilityChanged(visibility);
        }
    }

這里面就是調(diào)用了所有子View的dispatchWindowVisibilityChanged:

    /**
     * Dispatch a window visibility change down the view hierarchy.
     * ViewGroups should override to route to their children.
     *
     * @param visibility The new visibility of the window.
     *
     * @see #onWindowVisibilityChanged(int)
     */
    public void dispatchWindowVisibilityChanged(@Visibility int visibility) {
        onWindowVisibilityChanged(visibility);
    }

View中調(diào)用的onWindowVisibilityChanged方法挫鸽,既然我們今天的主角是SurfaceView说敏,那就去看SurfaceView是如何重寫這個(gè)方法的:

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        mWindowVisibility = visibility == VISIBLE;
        updateRequestedVisibility();
        updateSurface();
    }

這里需要重點(diǎn)說(shuō)下updateSurface這個(gè)核心方法:

    /** @hide */
    protected void updateSurface() {
        //判斷SurfaceView的宿主窗口的大小是否已經(jīng)計(jì)算好了,
        //SurfaceView必須在宿主窗口大小計(jì)算完畢之后才能更新
        if (!mHaveFrame) {
            return;
        }
        ViewRootImpl viewRoot = getViewRootImpl();
        
        ...代碼省略...
        //檢查surfaceview是否存在變化
        if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
            getLocationInWindow(mLocation);

            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                    + "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mWindowSpaceLeft != mLocation[0])
                    + " top=" + (mWindowSpaceTop != mLocation[1]));

            try {
               ...代碼省略...

                if (creating) {
                    //創(chuàng)建SurfaceSession
                    mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
                    mDeferredDestroySurfaceControl = mSurfaceControl;

                    updateOpaqueFlag();
                    通過(guò)SurfaceSession創(chuàng)建SurfaceControl
                    mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession,
                            "SurfaceView - " + viewRoot.getTitle().toString(),
                            mSurfaceWidth, mSurfaceHeight, mFormat,
                            mSurfaceFlags);
                } else if (mSurfaceControl == null) {
                    return;
                }

                boolean realSizeChanged = false;

                mSurfaceLock.lock();
                try {
                    mDrawingStopped = !visible;

                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                            + "Cur surface: " + mSurface);

                    SurfaceControl.openTransaction();
                    try {
                        //設(shè)置Layer屬性
                        mSurfaceControl.setLayer(mSubLayer);
                       ...代碼省略...
                    } finally {
                        SurfaceControl.closeTransaction();
                    }

                    if (sizeChanged || creating) {
                        redrawNeeded = true;
                    }
                     ...代碼省略...
                } finally {
                    mSurfaceLock.unlock();
                }

                try {
                     ...代碼省略...

                    if (creating) {
                        //mSurface從mSurfaceControl中拷貝出GraphicBufferProducer
                        mSurface.copyFrom(mSurfaceControl);
                    }

                   ...代碼省略...
            } catch (Exception ex) {
                Log.e(TAG, "Exception configuring surface", ex);
            }
            ...代碼省略...
        }
    }
  • 這里我們首先從創(chuàng)建SurfaceSession開始:
public final class SurfaceSession {
    // Note: This field is accessed by native code.
    private long mNativeClient; // SurfaceComposerClient*

    private static native long nativeCreate();
    private static native long nativeCreateScoped(long surfacePtr);
    private static native void nativeDestroy(long ptr);
    private static native void nativeKill(long ptr);

    /** Create a new connection with the surface flinger. */
    public SurfaceSession() {
        mNativeClient = nativeCreate();
    }

    public SurfaceSession(Surface root) {
        mNativeClient = nativeCreateScoped(root.mNativeObject);
    }

    /* no user serviceable parts here ... */
    @Override
    protected void finalize() throws Throwable {
        try {
            if (mNativeClient != 0) {
                nativeDestroy(mNativeClient);
            }
        } finally {
            super.finalize();
        }
    }

    /**
     * Forcibly detach native resources associated with this object.
     * Unlike destroy(), after this call any surfaces that were created
     * from the session will no longer work.
     */
    public void kill() {
        nativeKill(mNativeClient);
    }
}

SurfaceSession本身代碼量非常少丢郊,在構(gòu)造方法中調(diào)用了nativeCreateScoped(root.mNativeObject)方法盔沫,那么就進(jìn)入對(duì)應(yīng)的cpp文件看下具體實(shí)現(xiàn):

frameworks/base/core/jni/android_view_SurfaceSession.cpp

static jlong nativeCreateScoped(JNIEnv* env, jclass clazz, jlong surfaceObject) {
    Surface *parent = reinterpret_cast<Surface*>(surfaceObject);
    SurfaceComposerClient* client = new SurfaceComposerClient(parent->getIGraphicBufferProducer());
    client->incStrong((void*)nativeCreate);
    return reinterpret_cast<jlong>(client);
}

這里創(chuàng)建了SurfaceComposerClient對(duì)象,調(diào)用了SurfaceComposerClient的父類方法IncStrong医咨,內(nèi)部會(huì)調(diào)用SurfaceComposerClient#onFirstRef方法

/frameworks/native/libs/gui/SurfaceComposerClient.cpp

void SurfaceComposerClient::onFirstRef() {
    //獲取SurfacFlinger的代理BpSurfaceComposer
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    if (sf != 0 && mStatus == NO_INIT) {
        auto rootProducer = mParent.promote();
        sp<ISurfaceComposerClient> conn;
        conn = (rootProducer != nullptr) ? sf->createScopedConnection(rootProducer) :
                sf->createConnection();
        if (conn != 0) {
            mClient = conn;
            mStatus = NO_ERROR;
        }
    }
}

調(diào)用BpSurfaceComposer的createScopedConnection方法,然后通過(guò)Binder調(diào)用SurfaceFlinger的createScopedConnection

sp<ISurfaceComposerClient> SurfaceFlinger::createScopedConnection(
        const sp<IGraphicBufferProducer>& gbp) {
    if (authenticateSurfaceTexture(gbp) == false) {
        return nullptr;
    }
    const auto& layer = (static_cast<MonitoredProducer*>(gbp.get()))->getLayer();
    if (layer == nullptr) {
        return nullptr;
    }

   return initClient(new Client(this, layer));
}

創(chuàng)建對(duì)應(yīng)的BpSurfaceComposerClient架诞,然后將Client返回給Java層的SurfaceSession拟淮,并賦值給mNativeClient.

  • 然后我們?cè)賮?lái)看下SurfaceControl的創(chuàng)建過(guò)程
 mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession,
           "SurfaceView - " + viewRoot.getTitle().toString(),
           mSurfaceWidth, mSurfaceHeight, mFormat,
           mSurfaceFlags);

咱們重新回到updateSurface方法中,此處初始化了SurfaceControl谴忧,將SurfaceSession傳進(jìn)去很泊,再來(lái)看下構(gòu)造方法里面都做了什么操作

        public SurfaceControlWithBackground(SurfaceSession s,
                        String name, int w, int h, int format, int flags)
                       throws Exception {
            super(s, name, w, h, format, flags);
            mBackgroundControl = new SurfaceControl(s, "Background for - " + name, w, h,
                    PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
            mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
        }
    public SurfaceControl(SurfaceSession session,
            String name, int w, int h, int format, int flags)
                    throws OutOfResourcesException {
        this(session, name, w, h, format, flags, null, INVALID_WINDOW_TYPE, Binder.getCallingUid());
    }

    public SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags,
            SurfaceControl parent, int windowType, int ownerUid)
                    throws OutOfResourcesException {
        if (session == null) {
            throw new IllegalArgumentException("session must not be null");
        }
        if (name == null) {
            throw new IllegalArgumentException("name must not be null");
        }

        if ((flags & SurfaceControl.HIDDEN) == 0) {
            Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set "
                    + "to ensure that they are not made visible prematurely before "
                    + "all of the surface's properties have been configured.  "
                    + "Set the other properties and make the surface visible within "
                    + "a transaction.  New surface name: " + name,
                    new Throwable());
        }

        mName = name;
        mNativeObject = nativeCreate(session, name, w, h, format, flags,
            parent != null ? parent.mNativeObject : 0, windowType, ownerUid);
        if (mNativeObject == 0) {
            throw new OutOfResourcesException(
                    "Couldn't allocate SurfaceControl native object");
        }

        mCloseGuard.open("release");
    }

SurfaceControlWithBackground的構(gòu)造方法主要調(diào)用了父類的構(gòu)造方法,然后父類SurfaceControl中調(diào)用了nativeCreate方法沾谓,那么再次進(jìn)入C++層看下那提測(cè)Create的處理邏輯:

frameworks\native\services\surfaceflinger\ android_view_SurfaceControl.cpp

static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
        jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
        jint windowType, jint ownerUid) {
    ScopedUtfChars name(env, nameStr);
    sp<SurfaceComposerClient> client(android_view_SurfaceSession_getClient(env, sessionObj));
    SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
   //client為SurfaceComposerClient對(duì)象
    sp<SurfaceControl> surface;
    status_t err = client->createSurfaceChecked(
            String8(name.c_str()), w, h, format, &surface, flags, parent, windowType, ownerUid);
     ...代碼省略...
    //增加SurfaceControl的引用計(jì)數(shù)
    surface->incStrong((void *)nativeCreate);
    //獲取IGraphicBufferProducer對(duì)象
    return reinterpret_cast<jlong>(surface.get());
}

首先通過(guò)sessionObj獲取SurfaceComposerClient的代理對(duì)象BpSurfaceComposerClient委造,然后通過(guò)SurfaceComposerClient創(chuàng)建Surface

frameworks\native\libs\gui\SurfaceComposerClient.cpp

status_t SurfaceComposerClient::createSurfaceChecked(
        const String8& name,
        uint32_t w,
        uint32_t h,
        PixelFormat format,
        sp<SurfaceControl>* outSurface,
        uint32_t flags,
        SurfaceControl* parent,
        int32_t windowType,
        int32_t ownerUid)
{
    sp<SurfaceControl> sur;
    status_t err = mStatus;

    if (mStatus == NO_ERROR) {
        sp<IBinder> handle;
        sp<IBinder> parentHandle;
        sp<IGraphicBufferProducer> gbp;

        if (parent != nullptr) {
            parentHandle = parent->getHandle();
        }
        err = mClient->createSurface(name, w, h, format, flags, parentHandle,
                windowType, ownerUid, &handle, &gbp);
        ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
        if (err == NO_ERROR) {
            *outSurface = new SurfaceControl(this, handle, gbp, true /* owned */);
        }
    }
    return err;
}

此處mClient是在剛才創(chuàng)建SurfaceSession的時(shí)候生成的,SurfaceFlinger為BnSurfaceComposerClient創(chuàng)建的一個(gè)代理對(duì)象即BpSurfaceComposerClient對(duì)象均驶。然后調(diào)用對(duì)應(yīng)的createSurface方法昏兆,如果沒有出現(xiàn)錯(cuò)誤,那么就直接創(chuàng)建出SurfaceControl辣恋。那么我們?cè)倏聪耤reateSurface的邏輯:

frameworks\native\services\surfaceflinger\Client.cpp

status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        const sp<IBinder>& parentHandle, int32_t windowType, int32_t ownerUid,
        sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp)
{
   ...代碼省略...

    sp<MessageBase> msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle,
            windowType, ownerUid, gbp, &parent);
    mFlinger->postMessageSync(msg);
    return static_cast<MessageCreateLayer*>( msg.get() )->getResult();
}

這里通過(guò)跨進(jìn)程的RPC通信亮垫,調(diào)用SurfaceFlinger進(jìn)程的createLayer方法:

frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp

status_t SurfaceFlinger::createLayer(
        const String8& name,
        const sp<Client>& client,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
        sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent)
{
   ...代碼省略...
   status_t result = NO_ERROR;

    sp<Layer> layer;

    String8 uniqueName = getUniqueLayerName(name);

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceNormal:
            result = createBufferLayer(client,
                    uniqueName, w, h, flags, format,
                    handle, gbp, &layer);

            break;
        case ISurfaceComposerClient::eFXSurfaceColor:
            result = createColorLayer(client,
                    uniqueName, w, h, flags,
                    handle, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }
...代碼省略...
    //將client和Layer關(guān)聯(lián)起來(lái),把Layer添加到client的mLayers集合里面去
    result = addClientLayer(client, *handle, *gbp, layer, *parent);
    if (result != NO_ERROR) {
        return result;
    }
    mInterceptor->saveSurfaceCreation(layer);

    setTransactionFlags(eTransactionNeeded);
    return result;
}

此處調(diào)用createNormalLayer創(chuàng)建layer伟骨,然后將Layer與client關(guān)聯(lián)起來(lái)饮潦。SurfaceFlinger里的Layer對(duì)應(yīng)的就是Java層的Surface。
所以初始化SurfaceControl的時(shí)候就是創(chuàng)建了一個(gè)SurfaceControl,并讓SurfaceFlinger創(chuàng)建了一個(gè)對(duì)應(yīng)的Layer携狭。

  • 接下來(lái)就重新回到j(luò)ava層继蜡,再來(lái)看下初始化完SurfaceControl以后,就將SurfaceControl的GraphicBufferProducer copy給mSurface對(duì)象
                    if (creating) {
                        mSurface.copyFrom(mSurfaceControl);
                    }

至此逛腿,Surface就創(chuàng)建完畢了稀并,下一節(jié)再講SurfaceView是如何"挖洞"的


總結(jié)

SurfaceView創(chuàng)建Surface主要的過(guò)程如下:

  1. 首先創(chuàng)建SurfaceSession,這個(gè)時(shí)候返回一個(gè)SurfaceComposeClient的指針給java層
  2. SurfaceComposeClient在創(chuàng)建的時(shí)候单默,會(huì)獲取SurfaceFlinger的代理對(duì)象BpSurfaceComposer碘举,通過(guò)該對(duì)象在SurfaceFlinger中創(chuàng)建BnSurfaceComposerClient對(duì)象。并且將該對(duì)象創(chuàng)建BpSurfaceComposerClient對(duì)象賦值給了SurfaceComposeClient的mClient
  3. 然后創(chuàng)建SurfaceControl搁廓,獲取SurfaceComposeClient的代理對(duì)象BpSurfaceComposeClient,由改對(duì)象調(diào)用createSurface通過(guò)RPC通信調(diào)用到SurfaceFlinger的createSurface方法
  4. 創(chuàng)建Layer對(duì)象
  5. 將SurfaceControl拷貝給mSurface對(duì)象引颈,copy過(guò)程中創(chuàng)建對(duì)應(yīng)的Surface對(duì)象,然后將C++層的Surface賦值給Java層的Surface對(duì)象的mNativeObject
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末境蜕,一起剝皮案震驚了整個(gè)濱河市蝙场,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌粱年,老刑警劉巖售滤,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡完箩,警方通過(guò)查閱死者的電腦和手機(jī)赐俗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嗜憔,“玉大人秃励,你說(shuō)我怎么就攤上這事〖罚” “怎么了夺鲜?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)呐舔。 經(jīng)常有香客問我币励,道長(zhǎng),這世上最難降的妖魔是什么珊拼? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任食呻,我火速辦了婚禮,結(jié)果婚禮上澎现,老公的妹妹穿的比我還像新娘仅胞。我一直安慰自己,他們只是感情好剑辫,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布干旧。 她就那樣靜靜地躺著,像睡著了一般妹蔽。 火紅的嫁衣襯著肌膚如雪椎眯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天胳岂,我揣著相機(jī)與錄音编整,去河邊找鬼。 笑死乳丰,一個(gè)胖子當(dāng)著我的面吹牛掌测,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播产园,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼汞斧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了淆两?” 一聲冷哼從身側(cè)響起断箫,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拂酣,失蹤者是張志新(化名)和其女友劉穎秋冰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婶熬,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剑勾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年埃撵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片虽另。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暂刘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捂刺,到底是詐尸還是另有隱情谣拣,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布族展,位于F島的核電站森缠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏仪缸。R本人自食惡果不足惜贵涵,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恰画。 院中可真熱鬧宾茂,春花似錦、人聲如沸拴还。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)自沧。三九已至坟奥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拇厢,已是汗流浹背爱谁。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留孝偎,地道東北人访敌。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像衣盾,于是被迫代替她去往敵國(guó)和親寺旺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355