Android_窗口繪制相關(guān)流程

看到setContentView時(shí), 感覺又要把Activity的啟動(dòng)流程看一遍, 然后又要繼續(xù)往上看, 不知道何時(shí)才能正式進(jìn)入窗口繪制流程, 下面先把需要做的準(zhǔn)備工作記錄下來:

1、Windows.java->Callback.class;

1、先從ActivityThread入手:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    ...
    Activity a = performLaunchActivity(r, customIntent);
    ...
}
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    }
    ...
    if (activity != null) {
        Context appContext = createBaseContextForActivity(r, activity);
        ...
        Window window = null;
        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);
    ...
    }
}
1立润、ClassLoader加載對應(yīng)的Activity.class文件
2、createBaseContextForActivity(r, activity);
private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
    ...
    ContextImpl appContext = ContextImpl.createActivityContext(this, r.packageInfo, r.token, displayId, r.overrideConfig);
    appContext.setOuterContext(activity);
    Context baseContext = appContext;
    ...
    return baseContext;
}
3空镜、activity.attach():

2、Activity.java

final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                  Window window) {
        attachBaseContext(context);
        mWindow = new PhoneWindow(this, window);
        mWindow.setCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        mInstrumentation = instr;
        mToken = token;
        mActivityInfo = info;
        mParent = parent;
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        mWindowManager = mWindow.getWindowManager();
    }
  • 調(diào)用setCallback將window與activity產(chǎn)生關(guān)聯(lián), 而window實(shí)際上是PhoneWindow; 切入到Window中去觀察setCallback();
public void setCallback(Callback callback) {
    mCallback = callback;
}
public interface Callback {
    ...
    public boolean dispatchTouchEvent(MotionEvent event);
    public void onWindowFocusChanged(boolean hasFocus);
    ...
}
  • Callback是一系列的回調(diào)函數(shù)的集合, 先記下來以后再看看是如何實(shí)現(xiàn)回調(diào)的

從上面代碼可以試著分析一波WindowManager, ViewManager, WindowManagerImpl, PhoneWindow, Window的關(guān)系

public class PhoneWindow extends Window{...}
public abstract class Window{...}
public final class WindowManagerImpl implements WindowManager{...}
public interface WindowManager extends ViewManager {...}
public interface ViewManager{...}
Window mWindow = new PhoneWindow(this, window);
public PhoneWindow(Context context) {
    super(context);
    mLayoutInflater = LayoutInflater.from(context);
}
public PhoneWindow(Context context, Window preservedWindow) {
    this(context);
}

  • 將mWindow實(shí)際上指向子類PhoneWindow;
mWindow.setWindowManager(
           (WindowManager)context.getSystemService(Context.WINDOW_SERVICE), 
            mToken, 
            mComponent.flattenToString(), 
            (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
      if (wm == null) {
          wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
      }
      mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
private WindowManager mWindowManager;
private WindowManagerImpl(Context context, Window parentWindow) {
      mContext = context;
      mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
      return new WindowManagerImpl(mContext, parentWindow);
}
private final Window mParentWindow;
  • 1捌朴、WindowManagerImpl中的mParent實(shí)際上指向的是PhoneWindow;
  • 2吴攒、mWindowManager實(shí)際上指向的WindowManagerImpl;
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ...
    if (r.isPersistable()) {
        mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
    } else {
        mInstrumentation.callActivityOnCreate(activity, r.state);
    }
    ...
}
  • 1、isPersistable關(guān)于持久化存儲(chǔ)的一個(gè)玩意兒, 先記下來, 留待以后分析
  • 2男旗、目前先重點(diǎn)分析callActivityOnCreate(activity, r.state)方法;
public class Instrumentation {
      public void callActivityOnCreate(Activity activity, Bundle icicle) {
          prePerformCreate(activity);
          activity.performCreate(icicle);
          postPerformCreate(activity);
      }
}
class->Instrumentation->
public void callActivityOnCreate(Activity activity, Bundle icicle) {
      prePerformCreate(activity);
      activity.performCreate(icicle);
      postPerformCreate(activity);
}
class->Activity->
final void performCreate(Bundle icicle) {
      restoreHasCurrentPermissionRequest(icicle);
      onCreate(icicle);
      mActivityTransitionState.readState(icicle);
      performCreateCommon();
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
      if (mLastNonConfigurationInstances != null) {
          mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
      }
      if (savedInstanceState != null) {
          mFragments.restoreAllState(p, mLastNonConfigurationInstances != null
                    ? mLastNonConfigurationInstances.fragments : null);
      }
      mFragments.dispatchCreate();
}
  • 1舶斧、onCreate()方法由performLaunchActivity調(diào)用
  • 2、注意到mFragments與attach方法中都有mFragments出現(xiàn), 先記下來, 以后分析.

**onCreate()方法被調(diào)用了, 我們通常在onCreate()里面調(diào)用setContentView()方法對窗口進(jìn)行繪制;

3察皇、setContentView:

class Activity->
public void setContentView(@LayoutRes int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}
public Window getWindow() {
    return mWindow;
}
  • 前面在attach()方法時(shí)已經(jīng)將PhoneWindow賦值給了Window. 所以切入到PhoneWindow
class PhoneWindow->
@Override
public void setContentView(int layoutResID) {
      if (mContentParent == null) {
          installDecor();
      }
}

第一次進(jìn)入onCreate方法調(diào)用setContentView方法時(shí), mContentParent=null; 切入到installDecor方法中

class PhoneWindow->
private void installDecor() {
    if (mDecor == null) {
        mDecor = generateDecor(-1);
    } 
    if (mContentParent == null) {
        mContentParent = generateLayout(mDecor);
    }
}

此時(shí)mDecor = null; 切入到generateDecor(-1)方法

class PhoneWindow->
protected DecorView generateDecor(int featureId) {
    return new DecorView(context, featureId, this, getAttributes());
}
class DecorView->
DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params) {
    super(context);
    setWindow(window);
}
void setWindow(PhoneWindow phoneWindow) {
    mWindow = phoneWindow;
}
private PhoneWindow mWindow;
  • 1茴厉、創(chuàng)建DecorView對象, 并對DecorView中的PhoneWindow進(jìn)行賦值.
  • 2泽台、前文中的attach種進(jìn)行了mWindow.setCallback(this)將PhoneWindow, Activity, Callback三者綁定了關(guān)系, 這里又將PhoneWindow賦值給DecorView中的PhoneWindow, 也就是將PhoneWindow, Activity, Callback, DecorView四者產(chǎn)生了關(guān)聯(lián);

然后切入到generateLayout();方法

protected ViewGroup generateLayout(DecorView decor) {
    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
    return contentParent;
}

暫時(shí)還沒發(fā)生contentParent的作用, 先記下, 以后遇到大佬趕緊問一波

目前發(fā)現(xiàn)好像就只做了幾件事:

1、初始化DecorView,將PhoneWindow, Activity, Callback, DecorView產(chǎn)生聯(lián)系;
2矾缓、初始化ViewGroup mContentParent;

然后繼續(xù)切入到handleLaunchActivity方法:

class ActivityThread->
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
    }
}

4怀酷、resume->繪制三部曲:

繼續(xù)進(jìn)入到handleResumeActivity()方法發(fā)現(xiàn)里面依次調(diào)用了measure, layout, draw方法;但是流程感覺很復(fù)雜, 估計(jì)這次看完, 后面有時(shí)間還會(huì)回來反復(fù)再看幾遍.

class ActivityThread->
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ...
    ActivityClientRecord r = mActivities.get(token);
    r = performResumeActivity(token, clearHide, reason);
    ...
}
class ActivityThread->
public final ActivityClientRecord performResumeActivity(IBinder token, boolean clearHide, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    if (r != null && !r.activity.mFinished) {
        try {
             r.activity.performResume();
        } 
    }
    return r;
}
class Activity->
final void performResume() {
    performRestart();
    mFragments.execPendingActions();
    mInstrumentation.callActivityOnResume(this);
    mFragments.dispatchResume();
    mFragments.execPendingActions();
    onPostResume();
}
class Instrumentation->
public void callActivityOnResume(Activity activity) {
    activity.onResume();
}
  • 貌似performResumeActivity()就做了一件事, 調(diào)用activity的onResume()方法;

然后繼續(xù)handleResumeActivity方法:

class ActivityThread->
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    r = performResumeActivity(token, clearHide, reason);
    if (r != null) {
        final Activity a = r.activity;
        if (r.window == null && !a.mFinished && willBeVisible) {
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            ViewManager wm = a.getWindowManager();
            a.mDecor = decor;
            if (a.mVisibleFromClient && !a.mWindowAdded) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }
        } 
        if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
            if (r.activity.mVisibleFromClient) {
                r.activity.makeVisible();
            }
        }
    } 
}
class Activity->
void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}
  • 1、r.window = activity.getWindow()將PhoneWindow賦值給ActivityClientRecord中的Window;
  • 2嗜闻、a.getWindowManager()將WindowManagerImpl賦值給ViewManager;
  • 3蜕依、wm.addView(decor, l);接下來重點(diǎn)分析一波
  • 4、然后調(diào)用mDecor.setVisibility(View.VISIBLE); 進(jìn)行UI顯示;

我們切入到addView里面去看看:

addView最終在WindowManagerImpl中進(jìn)行的實(shí)現(xiàn);
class WindowManagerImpl->
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
private final Window mParentWindow;
class WindowManagerGlobal->
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    ViewRootImpl root;
    View panelParentView = null;
    synchronized (mLock) {
       int index = findViewLocked(view, false);
       if (index >= 0) {
           root = new ViewRootImpl(view.getContext(), display);
           view.setLayoutParams(wparams);
           mViews.add(view);
           mRoots.add(root);
           mParams.add(wparams);
       }
       try {
           root.setView(view, wparams, panelParentView);
       } 
    }
}
private int findViewLocked(View view, boolean required) {
    final int index = mViews.indexOf(view);
    return index;
}
class ViewRootImpl->
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            mView = view;
            requestLayout();
        }
    }
}
View mView;

這部分代碼做了一下幾件事:

  • 1琉雳、創(chuàng)建ViewRootImpl對象
  • 2样眠、root.setView(view)將DecorView賦值給View;
  • 3、調(diào)用requestLayout()方法;

5翠肘、繪制三部曲--requestLayout()方法:

先看一看ViewRootImp的結(jié)構(gòu):

public final class ViewRootImpl implements ViewParent {...}
public interface ViewParent {...}

上面的實(shí)現(xiàn)關(guān)系可以看出來ViewRootImpl并不是一個(gè)View, 他實(shí)際上是View的管理工具;

class ViewRootImpl->
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        mLayoutRequested = true;
        scheduleTraversals();
    }
}
void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        performTraversals();
    }
}

performTraversals()行數(shù)太多

class ViewRootImpl->
private void performTraversals() {
    if (mFirst || windowShouldResize || insetsChanged || viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
        if (!mStopped || mReportNextDraw) {
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
        }
        if (didLayout) {
            performLayout(lp, mWidth, mHeight);
        }
        performDraw();
    }
}

1檐束、三部曲->performMeasure:

private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}
mView = DecorView;
  • 1、由前面代碼知道m(xù)View其實(shí)是decorView;
  • 2束倍、接下來是對decorView進(jìn)行測量;
public class DecorView extends FrameLayout {...}
public class FrameLayout extends ViewGroup {...}
public abstract class ViewGroup extends View implements ViewParent, ViewManager {...}
public class View {...}

上面繼承關(guān)系, measure方法只有View方法中有:

class View->
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    if (forceLayout || needsLayout) {
        int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            onMeasure(widthMeasureSpec, heightMeasureSpec);
        } 
    }
}
class FrameLayout->
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int count = getChildCount();
    for (int i = 0; i < count; i++) {
         final View child = getChildAt(i);
         if (mMeasureAllChildren || child.getVisibility() != GONE) {
             measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
         }
    }
}
protected void measureChildWithMargins(View child, 
                                       int parentWidthMeasureSpec, int widthUsed,
                                       int parentHeightMeasureSpec, int heightUsed) {
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

上面代碼做了下面幾件事:

  • 1被丧、調(diào)用DecorView中的measure方法, 該方法只在父類View中有
  • 2、所以調(diào)用View中的measure方法, 然后調(diào)用到里面的onMeasure()方法;
  • 3绪妹、而onMeasure()方法又只有父類FrameLayout中有, 所以執(zhí)行FrameLayout方法中的onMeasure方法. 該方法會(huì)遍歷子類, 然后依次調(diào)用子類的measure方法.

1甥桂、三部曲->performLayout:

class ViewRootImpl-->
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
        final View host = mView;
        try {
            host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
        }
    }
}
mView = DecorView;
class View-->
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
    boolean changed = isLayoutModeOptical(mParent) ? setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        onLayout(changed, l, t, r, b);
    }
}
class FrameLayout-->
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
    final int count = getChildCount();
    for (int i = 0; i < count; i++) {
         final View child = getChildAt(i);
         if (child.getVisibility() != GONE) {
             child.layout(childLeft, childTop, childLeft + width, childTop + height);
         }
    }
}

上面代碼做了下面幾件事:

  • 1、調(diào)用DecorView的layout方法, 實(shí)際上調(diào)用View的layout方法
  • 2邮旷、layout調(diào)用onLayout方法, onLayout方法被DecorView的父類FrameLayout方法重寫, 實(shí)際調(diào)用FrameLayout的onLayout方法
  • 3黄选、然后遍歷DecorView里面的控件, 并對遍歷的控件調(diào)用其layout方法;

到此僅僅是把DecorView, 及其控件的繪制流程搞清楚了, 但是具體的實(shí)現(xiàn)細(xì)節(jié)并沒有進(jìn)行分析, 比如onMeasure和onLayout中大量出現(xiàn)的measureSize, measureMode這種數(shù)據(jù)的一系列計(jì)算全部跳過了, 這個(gè)步驟先留著吧, 后邊如果有機(jī)會(huì)去在mac環(huán)境下一套源碼, 調(diào)試著去分析, 不然真沒法搞明白里面的道道兒

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市廊移,隨后出現(xiàn)的幾起案子糕簿,更是在濱河造成了極大的恐慌,老刑警劉巖狡孔,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蜂嗽,居然都是意外死亡苗膝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門植旧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辱揭,“玉大人,你說我怎么就攤上這事病附∥是裕” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵完沪,是天一觀的道長域庇。 經(jīng)常有香客問我嵌戈,道長,這世上最難降的妖魔是什么听皿? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任熟呛,我火速辦了婚禮,結(jié)果婚禮上尉姨,老公的妹妹穿的比我還像新娘庵朝。我一直安慰自己,他們只是感情好又厉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布九府。 她就那樣靜靜地躺著,像睡著了一般覆致。 火紅的嫁衣襯著肌膚如雪侄旬。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天篷朵,我揣著相機(jī)與錄音勾怒,去河邊找鬼。 笑死声旺,一個(gè)胖子當(dāng)著我的面吹牛笔链,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播腮猖,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鉴扫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了澈缺?” 一聲冷哼從身側(cè)響起坪创,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎姐赡,沒想到半個(gè)月后莱预,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡项滑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年依沮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枪狂。...
    茶點(diǎn)故事閱讀 39,992評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡危喉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出州疾,到底是詐尸還是另有隱情辜限,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布严蓖,位于F島的核電站薄嫡,受9級特大地震影響氧急,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜岂座,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一态蒂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧费什,春花似錦钾恢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至稿黍,卻和暖如春疹瘦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巡球。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工言沐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酣栈。 一個(gè)月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓险胰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親矿筝。 傳聞我的和親對象是個(gè)殘疾皇子起便,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容