Android Service原理簡介與源碼分析--Service的啟動過程

Service是Android另外一個常用的組件笛园,其啟動的過程與Activity有相似之處谋旦。Service的啟動過程遂蛀,我們也拆分成兩部分來介紹:ContextWrapper調用AMS的過程以及AMS調用ActivityThread啟動Service的過程匙铡。本文主要分為三部分:ContextWrapper調用AMS源碼分析、AMS調用ActivityThread啟動Service源碼分析底靠、總結√芈粒快速了解可以直接看總結苛骨。

1.ContextWrapper調用AMS

通常我們在Activity中啟動一個Service代碼類似下邊這樣:

        Intent intent = new Intent("ACTION");
        startService(intent);

startService方法的實現(xiàn)在ContextWrapper中:

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
public class ContextWrapper extends Context {
    @UnsupportedAppUsage
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
...
    @Override
     public ComponentName startService(Intent service) {
           return mBase.startService(service);
      }
...
}

上面的代碼其實很簡單篱瞎,startService方法調用后執(zhí)行了mBase.startService(),看代碼我們很明顯的發(fā)現(xiàn)mBase是個Context類型的。了解這個具體實現(xiàn)到底是什么痒芝,ContextWrapper如何初始化的就很重要俐筋,我們閃回到ActivityThread 啟動 Activity:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = 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, r.configCallback);
...
        return activity;
}

上面的代碼可以看出,傳入Activity的context是ContextImpl严衬,attach方法會把Activity的上下文appContext與傳入的ContextImpl關聯(lián)起來澄者。具體看下ContextImpl appContext的賦值的方法createBaseContextForActivity()

private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
       ...
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        // For debugging purposes, if the activity's package name contains the value of
        // the "debug.use-second-display" system property as a substring, then show
        // its content on a secondary display if there is one.
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if (pkgName != null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if (id != Display.DEFAULT_DISPLAY) {
                    Display display =
                            dm.getCompatibleDisplay(id, appContext.getResources());
                    appContext = (ContextImpl) appContext.createDisplayContext(display);
                    break;
                }
            }
        }
        return appContext;
    }

從上面的代碼可以看出appContext是ContextImpl類型的,也就是最上面的mBase是ContextImpl類型的请琳,那我們看下ContextImpl內startService是如何實現(xiàn)的:

@Override
    public ComponentName startService(Intent service) {
        warnIfCallingFromSystemProcess();
        return startServiceCommon(service, false, mUser);
    }

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
            UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess(this);
            //調用AMS方法
            ComponentName cn = ActivityManager.getService().startService(
                    mMainThread.getApplicationThread(), service,
                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                    getOpPackageName(), getAttributionTag(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                } else if (cn.getPackageName().equals("?")) {
                    throw new IllegalStateException(
                            "Not allowed to start service " + service + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

上面代碼中粱挡,startService方法調用了startServiceCommon方法《砭可以看到在startServiceCommon方法中询筏,調用了AMS的代理IActivityManager的startService方法。

2.AMS調用ActivityThread啟動Service

上面介紹完了ContextWrapper啟動AMS的過程竖慧,接著上面的內容嫌套,我看下AMS的startService方法:

@Override
    public ComponentName startService(IApplicationThread caller, Intent service,
            String resolvedType, boolean requireForeground, String callingPackage, int userId)
            throws TransactionTooLargeException {
        enforceNotIsolatedCaller("startService");
        // Refuse possible leaked file descriptors
        if (service != null && service.hasFileDescriptors() == true) {
            throw new IllegalArgumentException("File descriptors passed in Intent");
        }

        if (callingPackage == null) {
            throw new IllegalArgumentException("callingPackage cannot be null");
        }

        if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
        synchronized(this) {
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            ComponentName res;
            try {
                res = mServices.startServiceLocked(caller, service,
                        resolvedType, callingPid, callingUid,
                        requireForeground, callingPackage, userId);
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            return res;
        }
    }

根據上面的代碼,我們看到調用了mServices的startServiceLocked方法圾旨,mServices從上面的部分可以看到是ActiveServices類型的踱讨,那我們看下其內部又是如何實現(xiàn)startServiceLocked方法的,這惡搞方法比較長砍的,我們關注下重點的代碼段:

ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
            @Nullable String callingFeatureId, final int userId,
            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
       ...
        
        //查找對應的Service信息
        ServiceLookupResult res =
            retrieveServiceLocked(service, null, resolvedType, callingPackage,
                    callingPid, callingUid, userId, true, callerFg, false, false);
        if (res == null) {
            return null;
        }
        if (res.record == null) {
            return new ComponentName("!", res.permission != null
                    ? res.permission : "private to package");
        }

        ServiceRecord r = res.record;
        ...
        NeededUriGrants neededGrants = mAm.mUgmInternal.checkGrantUriPermissionFromIntent(
                service, callingUid, r.packageName, r.userId);
       ...
        //ServiceRecord記錄必要的Service信息
        r.lastActivity = SystemClock.uptimeMillis();
        r.startRequested = true;
        r.delayedStop = false;
        r.fgRequired = fgRequired;
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                service, neededGrants, callingUid));

        if (fgRequired) {//是否需要運行在前臺
            // We are now effectively running a foreground service.
            ServiceState stracker = r.getTracker();
            if (stracker != null) {
                stracker.setForeground(true, mAm.mProcessStats.getMemFactorLocked(),
                        r.lastActivity);
            }
            mAm.mAppOpsService.startOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName, null,
                    true, false, null, false);
        }

        final ServiceMap smap = getServiceMapLocked(r.userId);
        boolean addToStarting = false;
        if (!callerFg && !fgRequired && r.app == null
                && mAm.mUserController.hasStartedUserState(r.userId)) {
            ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
            if (proc == null || proc.getCurProcState() > ActivityManager.PROCESS_STATE_RECEIVER) {
                //如果調用不是來自于前臺的調用者痹筛,并且已經有其他后臺的Service啟動了,則延時啟動廓鞠。
                // 這是為了避免很多應用都處理注冊的廣播導致進程濫發(fā)信息帚稠。
                if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
                        + r + " in " + proc);
                if (r.delayed) {
                    // Service已經在延時執(zhí)行列表里了,保持Service繼續(xù)等待
                    if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
                    return r.name;
                }
                if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
                    //有其他運行的處理床佳,延時執(zhí)行Service
                    Slog.i(TAG_SERVICE, "Delaying start of: " + r);
                    smap.mDelayedStartList.add(r);
                    r.delayed = true;
                    return r.name;
                }
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
                addToStarting = true;
            } else if (proc.getCurProcState() >= ActivityManager.PROCESS_STATE_SERVICE) {
                // Service可以執(zhí)行
                addToStarting = true;
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                        "Not delaying, but counting as bg: " + r);
            } else if (DEBUG_DELAYED_STARTS) {
                StringBuilder sb = new StringBuilder(128);
                sb.append("Not potential delay (state=").append(proc.getCurProcState())
                        .append(' ').append(proc.adjType);
                String reason = proc.makeAdjReason();
                if (reason != null) {
                    sb.append(' ');
                    sb.append(reason);
                }
                sb.append("): ");
                sb.append(r.toString());
                Slog.v(TAG_SERVICE, sb.toString());
            }
        } else if (DEBUG_DELAYED_STARTS) {
          ...
        }

        if (allowBackgroundActivityStarts) {
            r.whitelistBgActivityStartsOnServiceStart();
        }
        //核心賦值
        ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);

        if (!r.mAllowWhileInUsePermissionInFgs) {
            r.mAllowWhileInUsePermissionInFgs =
                    shouldAllowWhileInUsePermissionInFgsLocked(callingPackage, callingPid,
                            callingUid, service, r, allowBackgroundActivityStarts);
        }
        return cmp;
    }

上面代碼中retrieveServiceLocked獲取與Service對應的ServiceRecord等信息翁锡。如果ServiceRecord不存在,方法內部通過PackageManagerService獲取ServiceRecord信息夕土。最后調用方法startServiceInnerLocked:

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
            boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
        ServiceState stracker = r.getTracker();
        if (stracker != null) {
            stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
        }
        r.callStart = false;
        ...
        String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
        if (error != null) {
            return new ComponentName("!!", error);
        }
...
        return r.name;
    }

這個方法比較簡單馆衔,主要是調用了bringUpServiceLocked,我們重點看下這個方法:

private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
            boolean whileRestarting, boolean permissionsReviewRequired)
            throws TransactionTooLargeException {
       ...
        if (!whileRestarting && mRestartingServices.contains(r)) {
            // 等待重啟不進行任何操作
            return null;
        }
...
        // 準備拉起Service,從重啟中移除
        // restarting state.
        if (mRestartingServices.remove(r)) {
            clearRestartingIfNeededLocked(r);
        }

        // 確保Service不再延時執(zhí)行怨绣,準備啟動Service
        if (r.delayed) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
            getServiceMapLocked(r.userId).mDelayedStartList.remove(r);
            r.delayed = false;
        }

      ...

        // Service已經啟動角溃, 所在Package不能停止.
        try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    r.packageName, false, r.userId);
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + r.packageName + ": " + e);
        }

        final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
        //獲取Service將要運行的進程名
        final String procName = r.processName;
        HostingRecord hostingRecord = new HostingRecord("service", r.instanceName);
        ProcessRecord app;

        if (!isolated) {
          //運行Service的進程是否存在
            app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
            if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
                        + " app=" + app);
            if (app != null && app.thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                    //啟動Service
                    realStartServiceLocked(r, app, execInFg);
                    return null;
                } catch (TransactionTooLargeException e) {
                    throw e;
                } catch (RemoteException e) {
                    Slog.w(TAG, "Exception when starting service " + r.shortInstanceName, e);
                }

            }
        } else {
            //如果Service在獨立的進程中運行, 每次調用startProcessLocked()方法
            //都會得到一個新的獨立的進程篮撑,如果目前在等待則另外啟動一個新的進程减细。
            // 為了處理這種情況,在Service內存儲當前獨立的進程是正要運行還是等待喚起赢笨。
            app = r.isolatedProc;
            if (WebViewZygote.isMultiprocessEnabled()
                    && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) {
                hostingRecord = HostingRecord.byWebviewZygote(r.instanceName);
            }
            if ((r.serviceInfo.flags & ServiceInfo.FLAG_USE_APP_ZYGOTE) != 0) {
                hostingRecord = HostingRecord.byAppZygote(r.instanceName, r.definingPackageName,
                        r.definingUid);
            }
        }

        //如果進程不存在則啟動進程未蝌, 把Service已經執(zhí)行的狀態(tài)存儲起來驮吱。
        if (app == null && !permissionsReviewRequired) {
            // TODO (chriswailes): Change the Zygote policy flags based on if the launch-for-service
            //  was initiated from a notification tap or not.
            //啟動進程
            if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated, false)) == null) {
                String msg = "Unable to launch app "
                        + r.appInfo.packageName + "/"
                        + r.appInfo.uid + " for service "
                        + r.intent.getIntent() + ": process is bad";
                Slog.w(TAG, msg);
                //記錄ServiceRecord
                bringDownServiceLocked(r);
                return msg;
            }
            if (isolated) {
                r.isolatedProc = app;
            }
        }

       ...

        if (r.delayedStop) {
            // 延時停止
            r.delayedStop = false;
            if (r.startRequested) {
                if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                        "Applying delayed stop (in bring up): " + r);
                stopServiceLocked(r);
            }
        }

        return null;
    }

核心的處理已經在上面代碼中注釋了,很明顯啟動Service的方法是realStartSeviceLocked萧吠,多余的:

 /**
     * Note the name of this method should not be confused with the started services concept.
     * The "start" here means bring up the instance in the client, and this method is called
     * from bindService() as well.
     */
    private final void realStartServiceLocked(ServiceRecord r,
            ProcessRecord app, boolean execInFg) throws RemoteException {
       ...
        boolean created = false;
        try {
            ...
            mAm.notifyPackageUse(r.serviceInfo.packageName,
                                 PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
            //創(chuàng)建Service
            app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                    app.getReportedProcState());
            r.postNotification();
            created = true;
        } catch (DeadObjectException e) {
            Slog.w(TAG, "Application dead when creating service " + r);
            mAm.appDiedLocked(app, "Died when creating service");
            throw e;
        } finally {
            ...
        }
       ...
    }

上面代碼可以看到創(chuàng)建Service調用了scheduleCreateService方法左冬, app.thread是IApplicationThread類型的,其實現(xiàn)是ActivityThread的內部類ApplicationThread纸型。具體看下ApplicationThread的scheduleCreateService方法:

 public final void scheduleCreateService(IBinder token,
                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
            updateProcessState(processState, false);
            CreateServiceData s = new CreateServiceData();
            s.token = token;
            s.info = info;
            s.compatInfo = compatInfo;

            sendMessage(H.CREATE_SERVICE, s);
        }

這個方法其實不是很復雜拇砰,封裝即將啟動的Service信息到CreateServiceData中,然后向H類發(fā)送消息:

 private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

H類是ActivityThread的內部類狰腌,繼承自Handler除破,這個在Android Activity原理簡介與源碼分析一文中有介紹。這是應用程序主線程的一個消息管理類琼腔,下面我們看下H累的sendMessage方法:

 public void handleMessage(Message msg) {
        switch (msg.what) {
             ...
             case CREATE_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                
          ...
        }
....
}

可以看到瑰枫,當消息是CREATE_SERVICE的時候,會調用handleCreateService方法:

private void handleCreateService(CreateServiceData data) {
        //  如果切到后臺后準備GC了丹莲,service要激活了光坝,所以需要跳過GC
        unscheduleGcIdler();
        
        //獲取將要啟動Service的應用程序的包信息
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
          //獲取類加載器
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            //創(chuàng)建Service實例
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
           ...
        }

        try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
            //創(chuàng)建Service上下文ContextImpl
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);
          
            //獲取應用程序進程
            Application app = packageInfo.makeApplication(false, mInstrumentation);
          //初始化Service
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            //啟動Service
            service.onCreate();
            //將Service加入到ActivityThread的成員變量mServices中,記錄Service
            mServices.put(data.token, service);
            try {
              //記錄已經執(zhí)行的Service
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
           ...
        }
    }

上面就是Service最終的啟動過程圾笨。

3.總結

這里總結不做過多的文字描述直接上序列圖來總結下Service的啟動過程教馆。

3.1 ContextWrapper調用AMS
ContextWrapper調用AMS
3.1 AMS調用ActivityThread啟動Service
AMS啟動Service

如有意見和建議歡迎大家留言逊谋,謝謝

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末擂达,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子胶滋,更是在濱河造成了極大的恐慌板鬓,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件究恤,死亡現(xiàn)場離奇詭異俭令,居然都是意外死亡,警方通過查閱死者的電腦和手機部宿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門抄腔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人理张,你說我怎么就攤上這事赫蛇。” “怎么了雾叭?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵悟耘,是天一觀的道長。 經常有香客問我织狐,道長暂幼,這世上最難降的妖魔是什么筏勒? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮旺嬉,結果婚禮上管行,老公的妹妹穿的比我還像新娘。我一直安慰自己鹰服,他們只是感情好病瞳,可當我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悲酷,像睡著了一般套菜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上设易,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天逗柴,我揣著相機與錄音,去河邊找鬼顿肺。 笑死戏溺,一個胖子當著我的面吹牛,可吹牛的內容都是我干的屠尊。 我是一名探鬼主播旷祸,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼讼昆!你這毒婦竟也來了托享?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤浸赫,失蹤者是張志新(化名)和其女友劉穎闰围,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體既峡,經...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡羡榴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了运敢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片校仑。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖传惠,靈堂內的尸體忽然破棺而出迄沫,到底是詐尸還是另有隱情,我是刑警寧澤涉枫,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布邢滑,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏困后。R本人自食惡果不足惜乐纸,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摇予。 院中可真熱鬧汽绢,春花似錦、人聲如沸侧戴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春壕吹,著一層夾襖步出監(jiān)牢的瞬間央拖,已是汗流浹背箫踩。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工腹备, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人回右。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓隆圆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翔烁。 傳聞我的和親對象是個殘疾皇子渺氧,可洞房花燭夜當晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內容