Android 系统状态栏和导航栏启动流程

我们知道当Android系统启动的时候会启动SystemServer,其中系统的主要服务都是通过它来启动的,本文就从这里开始一步一步研究系统状态栏和导航栏是怎么启动的。

首先我们先定位到SystemServer.run()方法中来,如下是方法的定义。

private void run() {

try {

Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitBeforeStartServices");

        .......

       ........

       ..........

        // Initialize the system context.

//创建系统上下文

        createSystemContext();

        // Create the system service manager.

        mSystemServiceManager =new SystemServiceManager(mSystemContext);

        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);

    }finally {

Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

    }

// Start services.

    try {

Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartServices");

        startBootstrapServices();

        startCoreServices();

        startOtherServices();

    .......

    ........

}

方法里面的内容比较多,我就列出主要的几个地方.

startBootstrapServices,startCoreServices,startOtherServices这个三个方法都是启动系统服务的,我们的系统状态栏和导航栏就是通过startOtherServices这个方法来间接启动的。

如下是方法的定义,当然也是列出我们主要关注的部分

private void startOtherServices() {

......

......

.......

//系统启动时候调用ActivityManagerService的systemReady方法

mActivityManagerService.systemReady(new Runnable() {

@Override

    public void run() {

Slog.i(TAG, "Making services ready");

        mSystemServiceManager.startBootPhase(

SystemService.PHASE_ACTIVITY_MANAGER_READY);

        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "PhaseActivityManagerReady");

        Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartObservingNativeCrashes");

        try {

mActivityManagerService.startObservingNativeCrashes();

        }catch (Throwable e) {

reportWtf("observing native crashes", e);

        }

Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

        if (!mOnlyCore) {

Slog.i(TAG, "WebViewFactory preparation");

            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "WebViewFactoryPreparation");

            mWebViewUpdateService.prepareWebViewInSystemServer();

            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

        }

Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");

        try {

startSystemUi(context);

        }catch (Throwable e) {

reportWtf("starting System UI", e);

        }

.......

......

.......


}

上面我们可以看到mActivityManagerService.systemReady,该方法里面会调用这个方法,这个是ActivityManagerService的方法,有名字就可以知道他是做一些系统启动的操作,在方法定义里面我们可以看到startSystemUi这个方法,点进去看看,方法的定义如下。

static final void startSystemUi(Context context) {

Intent intent =new Intent();

    intent.setComponent(new ComponentName("com.android.systemui",

                "com.android.systemui.SystemUIService"));

    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);

    //Slog.d(TAG, "Starting service: " + intent);

    context.startServiceAsUser(intent, UserHandle.SYSTEM);

}

我们看到他是通过Intent启动SystemUIService这个类,看名字就可以知道他是一个服务。

我们进入这个类的onCreate方法看看,

@Override

public void onCreate() {

super.onCreate();

    ((SystemUIApplication) getApplication()).startServicesIfNeeded();

}

我们看到他只调用了一个方法,我们接着点进去看看,

public void startServicesIfNeeded() {

startServicesIfNeeded(SERVICES);

}

SERVICES变量是个static,final类型的变量,

我们接着点进去看看

private void startServicesIfNeeded(Class[] services) {

if (mServicesStarted) {

return;

    }

if (!mBootCompleted) {

// check to see if maybe it was already completed long before we began

// see ActivityManagerService.finishBooting()

        if ("1".equals(SystemProperties.get("sys.boot_completed"))) {

mBootCompleted =true;

            if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");

        }

}

Log.v(TAG, "Starting SystemUI services for user " +

Process.myUserHandle().getIdentifier() +".");

    final int N = services.length;

    for (int i=0; i

Class cl = services[i];

        if (DEBUG) Log.d(TAG, "loading: " + cl);

        try {

Object newService = SystemUIFactory.getInstance().createInstance(cl);

            mServices[i] = (SystemUI) ((newService ==null) ? cl.newInstance() : newService);

        }catch (IllegalAccessException ex) {

throw new RuntimeException(ex);

        }catch (InstantiationException ex) {

throw new RuntimeException(ex);

        }

mServices[i].mContext =this;

        mServices[i].mComponents =mComponents;

        if (DEBUG) Log.d(TAG, "running: " +mServices[i]);

        mServices[i].start();

        if (mBootCompleted) {

mServices[i].onBootCompleted();

        }

}

mServicesStarted =true;

}

他的主要作用就是遍历SERVICES数组,启动里面的所有服务,如下是SERVICES数组的定义。

/**

* The classes of the stuff to start.

*/

private final Class[]SERVICES =new Class[] {

com.android.systemui.tuner.TunerService.class,

        com.android.systemui.keyguard.KeyguardViewMediator.class,

        com.android.systemui.recents.Recents.class,

        com.android.systemui.volume.VolumeUI.class,

        Divider.class,

        com.android.systemui.statusbar.SystemBars.class,

        com.android.systemui.usb.StorageNotification.class,

        com.android.systemui.power.PowerUI.class,

        com.android.systemui.media.RingtonePlayer.class,

        com.android.systemui.keyboard.KeyboardUI.class,

        com.android.systemui.tv.pip.PipUI.class,

        com.android.systemui.shortcut.ShortcutKeyDispatcher.class,

        com.android.systemui.VendorServices.class

};

我们看到这个数组里面定义了很多的服务(其实它们都不是服务,全是SystemUI),其中SystemBars就是我们今天要关注的服务,因为Android系统的状态栏和导航栏就是在这里面启动的。

从上面我们可以知道,在遍历初始化数组里面的类后,先调用了他们的start(),,如果系统启动完成了就会调用onBootCompleted()方法。我们先看看start方法到底干了什么。

@Override

public void start() {

if (DEBUG) Log.d(TAG, "start");

    mServiceMonitor =new ServiceMonitor(TAG, DEBUG,

            mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);

    mServiceMonitor.start();  // will call onNoService if no remote service is found

}

我们可以看到它是实例化了一个对象并且调用的它的start方法。

我们先看看它的构造方法

public ServiceMonitor(String ownerTag, boolean debug,

        Context context, String settingKey, Callbacks callbacks) {

mTag = ownerTag +".ServiceMonitor";

mDebug = debug;

mContext = context;

mSettingKey = settingKey;

mCallbacks = callbacks;

}

我们看到构造方法只是做了一个初始化操作,我们接着进入它的start方法。

public void start() {

// listen for setting changes

    ContentResolver cr =mContext.getContentResolver();

    cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),

            false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);

    // listen for package/component changes

    IntentFilter filter =new IntentFilter();

    filter.addAction(Intent.ACTION_PACKAGE_ADDED);

    filter.addAction(Intent.ACTION_PACKAGE_CHANGED);

    filter.addAction(Intent.ACTION_PACKAGE_REMOVED);

    filter.addDataScheme("package");

    mContext.registerReceiver(mBroadcastReceiver, filter);

    mHandler.sendEmptyMessage(MSG_START_SERVICE);

}

我们看到这个方法里面主要做了两件事情,一个就是注册广播监听安装包的添加,改变,移除,另外一件事情就是发送消息。所以我们进入handler去看看,它是怎么处理这条空消息的,

case MSG_START_SERVICE:

startService();

    break;


我们看到它调用了startService方法,我们接着点进去看看

private void startService() {

//从设置页面的共享参数里面间接的读取组件的包名和类名,具体什么包名和类名不在今天的范畴

mServiceName = getComponentNameFromSetting();

    if (mDebug) Log.d(mTag, "startService mServiceName=" +mServiceName);

    if (mServiceName ==null) {

mBound =false;

        mCallbacks.onNoService();

    }else {

long delay =mCallbacks.onServiceStartAttempt();

        mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);

    }

}


我们看到当mServiceName为null的时候会执行这个mCallbacks.onNoService()方法,由上面介绍的该类初始化我们知道这个回掉接口是从SystemBars传过来的,我们回到SystemBars类找到onNoService()方法,


@Override

public void onNoService() {

if (DEBUG) Log.d(TAG, "onNoService");

    createStatusBarFromConfig();  // fallback to using an in-process implementation

}

我们接着往下看

private void createStatusBarFromConfig() {

if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");

//从xml文件获取到类名

    final String clsName = mContext.getString(R.string.config_statusBarComponent);

    if (clsName ==null || clsName.length() ==0) {

throw andLog("No status bar component configured", null);

    }

Class cls =null;

    try {

//加载该类到虚拟机

cls = mContext.getClassLoader().loadClass(clsName);

    }catch (Throwable t) {

throw andLog("Error loading status bar component: " + clsName, t);

    }

try {

//实例化该类

mStatusBar = (BaseStatusBar) cls.newInstance();

    }catch (Throwable t) {

throw andLog("Error creating status bar component: " + clsName, t);

    }

mStatusBar.mContext = mContext;

    mStatusBar.mComponents = mComponents;

//调用它的start方法

    mStatusBar.start();

    if (DEBUG) Log.d(TAG, "started " +mStatusBar.getClass().getSimpleName());

}


这里xml定义的类名是PhoneStatusBar,我们直接进入它的start方法

@Override

public void start() {

//获取屏幕信息Display对象,里面保存了和屏幕有关的所有信息

mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))

//这个方法的作用是将屏幕的信息置为默认值。

.getDefaultDisplay();

//更新屏幕信息

    updateDisplaySize();

    mScrimSrcModeEnabled = mContext.getResources().getBoolean(

R.bool.config_status_bar_scrim_behind_use_src);

//这里面主要做了两件事,一个是初始化,一个是注册广播监听器

    super.start(); // calls createAndAddWindows()

    mMediaSessionManager

            = (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);

    // TODO: use MediaSessionManager.SessionListener to hook us up to future updates

    // in session state

//系统导航栏就是通过这个方法来添加的

    addNavigationBar();

    // Lastly, call to the icon policy to install/update all the icons.

//这是PhoneStatusBar的代理对象,一看就知道这里采用了代理模式

    mIconPolicy =new PhoneStatusBarPolicy(mContext, mIconController, mCastController,

            mHotspotController, mUserInfoController, mBluetoothController,

            mRotationLockController, mNetworkController.getDataSaverController());

    mIconPolicy.setCurrentUserSetup(mUserSetup);

    mSettingsObserver.onChange(false); // set up

    mHeadsUpObserver.onChange(true); // set up

    if (ENABLE_HEADS_UP) {

mContext.getContentResolver().registerContentObserver(

Settings.Global.getUriFor(Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED), true,

                mHeadsUpObserver);

        mContext.getContentResolver().registerContentObserver(

Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,

                mHeadsUpObserver);

    }

mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);

    mUnlockMethodCache.addListener(this);

    startKeyguard();

    KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);

    mDozeServiceHost =new DozeServiceHost();

    putComponent(DozeHost.class, mDozeServiceHost);

    putComponent(PhoneStatusBar.class, this);

    setControllerUsers();

    notifyUserAboutHiddenNotifications();

    mScreenPinningRequest =new ScreenPinningRequest(mContext);

    mFalsingManager = FalsingManager.getInstance(mContext);

}

,我们看到上面调用了super.start()方法,我们的状态栏就是在这里初始化的,我们进入这个方法定义


public void start() { 

 mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE); 

 mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); 

 mDisplay = mWindowManager.getDefaultDisplay(); 

 mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); 

 mNotificationData = new NotificationData(this); 

.........

.........

.......

        createAndAddWindows();

.........

.........

.......

    }


中间省略了大部分代码,我们主要看createAndAddWindows这个方法,看方法名称就可以知道,这个类是的功能是创建视图并且添加到window窗口,一下是方法的定义

@Override

    public void createAndAddWindows() {

        addStatusBarWindow();

    }

接着我们进入addStatusBarWindow这个方法,

private void addStatusBarWindow() {

//这个类主要是创建了系统状态栏,并且设置了一些监听器,比如状态栏的手势事件,电池电量改变监听器,sim卡状态改变监听器等等。

        makeStatusBarView();

        mStatusBarWindowManager = new StatusBarWindowManager(mContext);

        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,

                mHeadsUpManager);

//getStatusBarHeight这个方法是从xml中获取已经定义好的状态栏高度值,mStatusBarWindowManager.add这个方法的作用是将状态栏添加到Window中

        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());

    }

到此系统状态栏的创建流程就结束了,但是状态栏的手势事件是在哪里监听的呢,下面就来研究下手势状态栏手势事件的监听流程

由上面我们可以知道在添加状态栏的方法addStatusBarWindow中有个创建状态栏的方法makeStatusBarView,我们进入这个方法查看下,方法定义如下


protected PhoneStatusBarView makeStatusBarView() {

        final Context context = mContext;

        updateDisplaySize(); // populates mDisplayMetrics

        updateResources();

        inflateStatusBarWindow(context);

        mStatusBarWindow.setService(this);

        mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {

            @Override

            public boolean onTouch(View v, MotionEvent event) {

                checkUserAutohide(v, event);

//如果手势是刚点击状态栏(没有事件拦截)并且状态栏是展开的情况下,动画回收状态栏,否则跳到mStatusBarWindow的

onTouchEvent方法

                if (event.getAction() == MotionEvent.ACTION_DOWN) {

                    if (mExpandedVisible) {

                        animateCollapsePanels();

                    }

                }

                return mStatusBarWindow.onTouchEvent(event);

            }

        });

...

...

...

}

从这个方法中我们看到系统在创建状态栏的时候一道给它设置了触摸事件。如果状态栏实在展开的情况下,当用户点击了状态栏的没有事件拦截区域,此时状态栏会收缩并且带有动画效果。否则会进入StatusBarWindowView的onTouchEvent处理,那我们进入这个方法查看,如下是方法的定义


@Override

    public boolean onTouchEvent(MotionEvent ev) {

        boolean handled = false;

        if (mService.getBarState() == StatusBarState.KEYGUARD) {

            handled = mDragDownHelper.onTouchEvent(ev);

        }

        if (!handled) {

            handled = super.onTouchEvent(ev);

        }

        final int action = ev.getAction();

        if (!handled && (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL)) {

            mService.setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);

        }

        return handled;

    }

我们先进入mDragDownHelper.onTouchEvent(ev)这个方法查看,方法定义如下


@Override

    public boolean onTouchEvent(MotionEvent event) {

        if (!mDraggingDown) {

            return false;

        }

        final float x = event.getX();

        final float y = event.getY();

        switch (event.getActionMasked()) {

            case MotionEvent.ACTION_MOVE:

                mLastHeight = y - mInitialTouchY;

                captureStartingChild(mInitialTouchX, mInitialTouchY);

                if (mStartingChild != null) {

                    handleExpansion(mLastHeight, mStartingChild);

                } else {

                    mDragDownCallback.setEmptyDragAmount(mLastHeight);

                }

                if (mLastHeight > mMinDragDistance) {

                    if (!mDraggedFarEnough) {

                        mDraggedFarEnough = true;

                        mDragDownCallback.onCrossedThreshold(true);

                    }

                } else {

                    if (mDraggedFarEnough) {

                        mDraggedFarEnough = false;

                        mDragDownCallback.onCrossedThreshold(false);

                    }

                }

                return true;

            case MotionEvent.ACTION_UP:

                if (!isFalseTouch() && mDragDownCallback.onDraggedDown(mStartingChild,

                        (int) (y - mInitialTouchY))) {

                    if (mStartingChild == null) {

                        mDragDownCallback.setEmptyDragAmount(0f);

                    } else {

                        mCallback.setUserLockedChild(mStartingChild, false);

                    }

                    mDraggingDown = false;

                } else {

                    stopDragging();

                    return false;

                }

                break;

            case MotionEvent.ACTION_CANCEL:

                stopDragging();

                return false;

        }

        return false;

    }


当手势移动的时候会进入handleExpansion这个方法,它的定义如下


private void handleExpansion(float heightDelta, ExpandableView child) {

        if (heightDelta < 0) {

            heightDelta = 0;

        }

        boolean expandable = child.isContentExpandable();

        float rubberbandFactor = expandable

                ? RUBBERBAND_FACTOR_EXPANDABLE

                : RUBBERBAND_FACTOR_STATIC;

        float rubberband = heightDelta * rubberbandFactor;

        if (expandable

                && (rubberband + child.getCollapsedHeight()) > child.getMaxContentHeight()) {

            float overshoot =

                    (rubberband + child.getCollapsedHeight()) - child.getMaxContentHeight();

            overshoot *= (1 - RUBBERBAND_FACTOR_STATIC);

            rubberband -= overshoot;

        }

        child.setActualHeight((int) (child.getCollapsedHeight() + rubberband));

    }

我们看到了经过计算最终调用child.setActualHeight这个方法,设置状态栏的高度,来动态改变状态栏的显示高度,而松开手会调用接口回调这里就不深入研究了。到此系统状态栏的创建添加手势监听流程就彻底结束了。

接下来继续研究系统导航栏的创建添加过程。


接着上面我们看如下定义

public void start() {

.

.

createAndAddWindows();

.

.

.

}

此处省略了大部分代码,我们只关心和我们今天主题有关系的即可。

createAndAddWindows的实现实在PhoneStatusBar里




我们新进入addNavigationBar这个方法

// For small-screen devices (read: phones) that lack hardware navigation buttons

protected void addNavigationBar() {

if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mNavigationBarView);

    if (mNavigationBarView ==null)return;

    try {

WindowManagerGlobal.getWindowManagerService()

.watchRotation(new IRotationWatcher.Stub() {

@Override

            public void onRotationChanged(int rotation)throws RemoteException {

// We need this to be scheduled as early as possible to beat the redrawing of

// window in response to the orientation change.

                Message msg = Message.obtain(mHandler, () -> {

if (mNavigationBarView !=null

                            && mNavigationBarView.needsReorient(rotation)) {

repositionNavigationBar();

                    }

});

                msg.setAsynchronous(true);

                mHandler.sendMessageAtFrontOfQueue(msg);

            }

});

    }catch (RemoteException e) {

throw e.rethrowFromSystemServer();

    }

//

prepareNavigationBarView();

    mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams());

}


我们接着看prepareNavigationBarView这个方法,定义如下


private void prepareNavigationBarView() {

        mNavigationBarView.reorient();

        ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton();

        recentsButton.setOnClickListener(mRecentsClickListener);

        recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener);

        recentsButton.setLongClickable(true);

        recentsButton.setOnLongClickListener(mRecentsLongClickListener);

        ButtonDispatcher backButton = mNavigationBarView.getBackButton();

        backButton.setLongClickable(true);

        backButton.setOnLongClickListener(mLongPressBackListener);

        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();

        homeButton.setOnTouchListener(mHomeActionListener);

        homeButton.setOnLongClickListener(mLongPressHomeListener);

        mAssistManager.onConfigurationChanged();

    }

从这里我们看到它设置了系统状态栏的点击事件,长按事件,触摸事件等。


到此系统状态栏,导航栏的创建到事件监听大致流程分析完毕了,今天就到此结束吧。

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

推荐阅读更多精彩内容