Activity启动流程简直丧心病狂!

1.前言

小时候听大人们讲四大组件的故事,以为Activity就是手机屏幕上被看到的那东西。长大以后才发现,原来这个说法只是大人们照顾到孩子的理解能力所编造的谎言。那么今天,我们就从源码入手,亲眼去看一看Activity是如何启动的吧。

2.源码分析

Activity有2种启动的方式,前者是在Launcher界面点击应用的图标、后者是在应用中通过Intent进行跳转。我们主要介绍与后者相关的启动流程。

2.1 Instrumentation

故事要从Activity的startActivity()开始,这个方法会在内部调用startActivityForResult(),其中的核心代码长这样

Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);

也就是说,Activity是由mInstrumentation来启动的。查看Instrumentation类的
execStartActivity()

int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

在这里我们见到了老朋友ActivityManagerNative,它是用来与ActivityManagerService进行进程间通信的IBinder对象。

2.2 ActivityManagerService

回顾一下,在客户端进程中,我们会调用ActivityManagerProxy(Proxy)的startActivity(),经过AIDL回调服务端进程中ActivityManagerNative(Stub)的onTransact:

 @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        ...
        switch (code) {
        case START_ACTIVITY_TRANSACTION:
        {
            ...
         
            int result = startActivity(app, callingPackage, intent, resolvedType,
                    resultTo, resultWho, requestCode, startFlags, profilerInfo, options);
           ...
        }

如上所示,在服务端进程,也就是系统进程中,会真正调用ActivityManagerService的startActivity()方法。这里多嘴一句,ActivityManagerService在四大组件的创建以及生命周期管理中起了至关重要的作用。

startActivity()会转到startActivityAsUser(),这个方法会返回mStackSupervisor.startActivityMayWait()方法的结果。

2.3 ActivityStackSupervisor

现在皮球踢给了ActivityStackSupervisor。从名字上可以看出,这是一个Activity栈的管理类,其中有许多不同类型的list用来存放ActivityRecord。
在Android系统中,每个应用都有许多自己的栈以及相对应的Activity们,应用自己是不会管理这些仔们的生命周期的,它也管不来。每个应用所作的,就是将Activity的状态信息封装到ActivityRecord并交给系统级的服务——ActivityStackSupervisor来统一管理。因此ActivityStackSupervisor可以很轻松的获取到各种Activity。


    /** List of processes waiting to find out about the next visible activity. */
    final ArrayList<IActivityManager.WaitResult> mWaitingActivityVisible = new ArrayList<>();

    /** List of processes waiting to find out about the next launched activity. */
    final ArrayList<IActivityManager.WaitResult> mWaitingActivityLaunched = new ArrayList<>();

    /** List of activities that are ready to be stopped, but waiting for the next activity to
     * settle down before doing so. */
    final ArrayList<ActivityRecord> mStoppingActivities = new ArrayList<>();
    ...
  

startActivityMayWait()比较长,前面大段代码都是用来获取Activity的相关信息,比如FLAG、PID、UID等,关键代码1如下:

 ResolveInfo rInfo =
                                AppGlobals.getPackageManager().resolveIntent(
                                        intent, null,
                                        PackageManager.MATCH_DEFAULT_ONLY
                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
                            aInfo = rInfo != null ? rInfo.activityInfo : null;
                            aInfo = mService.getActivityInfoForUser(aInfo, userId);

2.3.1 PackageManagerService

这里负责去清单文件里查找要启动的Activity是否存在。我相信大部分同学都有这样的经历:直接copy一个Activity到自己的项目中,运行一跑,GG,其报错信息就是resolveIntent。
AppGlobals.getPackageManager()也是一个AIDL的过程,其返回的IBinder对象是IPackageManager,按照国际惯例,我们找到其本尊PackageManagerService的resolveIntent()方法:

 @Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId) {
        if (!sUserManager.exists(userId)) return null;
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
        return chooseBestActivity(intent, resolvedType, flags, query, userId);
    }

这里有一个List<ResolveInfo> 用来存符合要求的Activity,为什么会返回list呢?因为隐式启动时,有可能会匹配一个以上的activity,所以在最后return方法中,chooseBestActivity 就用来根据不同情况选择合适的Activity,比如优先级不同时自动返回最佳的、有默认值时直接返回默认的、优先级相同时让用户选择等等。

这里还要再介绍一下PackageManagerService,这是用来管理android中“包”的系统服务,它会扫描各个APK的AndroidManifest.xml中四大组件的注册信息并保存在自己那里,证据就在它自己的构造函数里:

File dataDir = Environment.getDataDirectory();
            mAppDataDir = new File(dataDir, "data");
            mAppInstallDir = new File(dataDir, "app");
            mAppLib32InstallDir = new File(dataDir, "app-lib");
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();
            mUserAppDataDir = new File(dataDir, "user");
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
            ...
            scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

            ...

而这个服务是在SystemServer中启动的,启动后会调用其main方法,并把自己交给系统服务的管理类(大多数系统服务都是这样玩的)

PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        ServiceManager.addService("package", m);

2.3.2 ActivityRecord

接下来回到ActivityStackSupervisor.startActivityMayWait()中,关键代码2如下:

int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                    componentSpecified, null, container, inTask);

这里调用了startActivityLocked,这个方法主要用来验证intent、Class、Permission等,其中有许多if语句对err进行了判断。

 if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
            // We couldn't find a class that can handle the given Intent.
            // That's the end of that!
            err = ActivityManager.START_INTENT_NOT_RESOLVED;
        }

判断完成之后,如果没有任何异常,就会创建一个ActivityRecord对象。从下面的构造方法中可以看出,ActivityRecord封装了与Activity有关的一系列参数,比如我是谁,我从哪里来,我要到哪里去。

ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
                intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
                requestCode, componentSpecified, voiceSession != null, this, container, options);

到此startActivityLocked()就基本完成了历史使命,在它代码的最后,新时代的接力棒又交到了startActivityUncheckedLocked()手上

err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);

2.4 ActivityStack

新时代一开始,就结合Flag与启动模式开始大清洗,得出当前Activity真正的启动模式

 final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
        final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
        final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;

        int launchFlags = intent.getFlags();
        if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
                (launchSingleInstance || launchSingleTask)) {
            // We have a conflict between the Intent and the Activity manifest, manifest wins.
            Slog.i(TAG, "Ignoring FLAG_ACTIVITY_NEW_DOCUMENT, launchMode is " +
                    "\"singleInstance\" or \"singleTask\"");
            launchFlags &=
                    ~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
        } 

获得启动模式后,自然要开始准备与Task有关的内容。比如判断是否要独立分配一个栈,如果需要则创建、如果不需要则再判断,是否要将当前Actitivy移动到当前Task的最前面。
这个方法里还有许多许多if判断语句,可见Activity作为四大组件之首果然名不虚传,其启动流程是极其复杂与严谨的。

final ActivityStack focusStack = getFocusedStack();
                    ActivityRecord curTop = (focusStack == null)
                            ? null : focusStack.topRunningNonDelayedActivityLocked(notTop);
                    boolean movedToFront = false;
                    if (curTop != null && (curTop.task != intentActivity.task ||curTop.task != focusStack.topTask())) {
                         ...
                            movedHome = true;
                         ...
                         }

我们不停的向下滑动鼠标,快速瞻仰一下Google工程师的伟大,慢慢就到了代码的最底部。

targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);

文章之前说过,ActivityStackSupervisor是用来管理ActivityStack的,在前面一万个判断中,我们已经获得了当前的ActivityStack以及封装Activity信息的ActivityRecord。所以接下来,就应该进入到ActivityStack中去执行具体的栈操作。

现在对ActivityStack本身操作。在系统中,对ActivityStack的管理也是基于栈这种结构的,暂且称之为stack栈。既然启动了新的Activity,那么该Activity所属的ActivityStack自然也要移动到stack栈的顶端。
下面有两种情况,一是Activity启动了新的ActivityStack:

   if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {
            insertTaskAtTop(rTask, r);
            mWindowManager.moveTaskToTop(taskId);
        }

此时将新的ActivityStack移动到stack栈的最顶端,同时也要将对应的Window置顶。

第二种情况是Activity还在原来的栈中运行,此时就将Activity移动到ActivityStack的顶端

        if (!newTask) {
         ...
         task.addActivityToTop(r);
         ...
                        
        }

2.5 pause与resume的江湖传说

在方法最后,代码又从ActivityStack回到了mStackSupervisor中:

 if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }

resumeTopActivitiesLocked中,首先判断了当前栈是否是Top栈,如果没有问题,就回到ActivityStack中

 if (isFrontStack(targetStack)) {
            result = targetStack.resumeTopActivityLocked(target, targetOptions);
        }

resumeTopActivityLocked()又调用了resumeTopActivityInnerLocked(),这个方法里重点就来了:

 // We need to start pausing the current activity so the top one
        // can be resumed...
        boolean dontWaitForPause = (next.info.flags&ActivityInfo.FLAG_RESUME_WHILE_PAUSING) != 0;
        boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, true, dontWaitForPause);
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
        }

看到这句注释了吗,江湖上一直流传的前一个Activity先pause,后一个Activity再resume的说法,原来是从这里来的!

2.5.1 onPause的故事

接着深入进去瞧瞧startPausingLocked()到底做了些什么。

 ActivityRecord prev = mResumedActivity;
 ...
 if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
            } catch (Exception e) {
                // Ignore exception, if process died other code will cleanup.
                Slog.w(TAG, "Exception thrown during pause", e);
                mPausingActivity = null;
                mLastPausedActivity = null;
                mLastNoHistoryActivity = null;
            }
        }

首先,这里有一个叫pre的ActivityRecord,顾名思义,这是当前Activity的上一个Activity。如果pre的应用不为空,则通过prev.app.thread.schedulePauseActivity()来执行具体的pause操作。
在这里,我们必须先来理清楚一个概念,ActivityStackSupervisor只是用来管理Activity与任务栈的,它并不具备执行具体操作的能力。
因此在这里是通过IPC告诉要暂停的Activity进入暂停。其具体的操作是在ActivityThread中执行的:

 public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                    configChanges);
        }

这里向Handler H发送了H.PAUSE_ACTIVITY的Message,H是这样处理回调的:

 case PAUSE_ACTIVITY:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&2) != 0);
                    maybeSnapshot();
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

handlePauseActivity()也是比较关键的方法

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
            ...
            //注释1
            performPauseActivity(token, finished, r.isPreHoneycomb());
            ...
            //注释2
            // Tell the activity manager we have paused.
            if (!dontReport) {
                try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

先看注释1处,这里调用了performPauseActivity()performPauseActivity()又调用了mInstrumentation.callActivityOnPause(r.activity);,这个方法长这样:

 public void callActivityOnPause(Activity activity) {
        activity.performPause();
    }

去Activity里看看performPause():

final void performPause() {
        mDoReportFullyDrawn = false;
        mFragments.dispatchPause();
        mCalled = false;
        onPause();
        mResumed = false;
        if (!mCalled && getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.GINGERBREAD) {
            throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onPause()");
        }
        mResumed = false;
    }

当当当当~绕了这么一大圈以后, onPause()方法终于得到了回调!

2.5.2 onPause之后的故事

现在回到handlePauseActivity()来看注释2,前面的代码已经执行了Activity的onPause(),所以现在要做的是将操作回馈给ActivityManagerService,来看看它的activityPaused()方法

 @Override
    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

因为目标Activity已经暂停了,所以现在ActivityManagerService需要知道该让哪一个Activity上位,此时只能去找被暂停的Activity所属的ActivityStack寻找答案。
activityPausedLocked()中调用了completePauseLocked(true)来告诉ActivityStack上一个Activity已经pause完成了:

if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if (!mService.isSleepingOrShuttingDown()) {
                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked(null);
                if (top == null || (prev != null && top != prev)) {
                    // If there are no more activities available to run,
                    // do resume anyway to start something.  Also if the top
                    // activity on the stack is not the just paused activity,
                    // we need to go ahead and resume it to ensure we complete
                    // an in-flight app switch.
                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
                }
            }
        }

所以接下来通过mStackSupervisor.getFocusedStack()获取顶层的ActivityStack,再执行mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null)来resume某个Activity。

至于为什么要通过mStackSupervisor来获取顶层栈而不是直接使用当前的ActivityStack,那是因为有可能被pause的Activity是当前ActivityStack中最后的Activity。

现在执行mStackSupervisor.resumeTopActivitiesLocked(),该方法调用了targetStack.resumeTopActivityLocked(target, targetOptions),于是我们再次从ActivityStackSupervisor回到ActivityStack,并且最终调用了resumeTopActivityInnerLocked()方法。

等等,这个过程是不是有点熟悉?往前找找,哦,原来之前通过startPausingLocked()开始pause一个Activity时也是走的这个流程,只不过现在改成resume另一个Activity了。

2.5.3 新进程的创建

我们来重新感受一下resumeTopActivityInnerLocked(),这里有一个很长if-else语句用来验证是否该需要启动的Activity所在进程和app已经存在,若存在,直接启动,否则准备创建该进程。

 if (next.app != null && next.app.thread != null) {
       ...
 } else {
       ...
            mStackSupervisor.startSpecificActivityLocked(next, true, true);
        }

我们重点关注创建进程的方法,这个startSpecificActivityLocked()再次进行了一波进程是否存在的判断,接着会调用ActivityManagerService的startProcessLocked()开始进程的创建。这个创建的过程也是相当凶残的,在这里就先简洁了当的介绍一下:

 if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);

Process.start()完成了ActivityThread的创建,之后就会执行ActivityThread的main()方法,这一步可谓是知识点丰富啊!

2.6 ActivityThread

 public static void main(String[] args) {
        ...

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        ...
        Looper.loop();

        ...
    }

大家所熟知的Looper终于在此闪亮登场,下一篇文章会详细介绍Looper机制。今天还是先把舞台交给thread.attach(false)。这个方法感觉熟悉吗?在之前的window介绍中,我们提到过mWindow是在Activity的attach()方法中初始化的,现在就的走进attach好好看一看吧。

 final IActivityManager mgr = ActivityManagerNative.getDefault();
 try {
                mgr.attachApplication(mAppThread);
            }

mgr是老朋友了,其本体是IActivityManager,也就是说这里通过AIDL调用了ActivityManagerService的attachApplication(mAppThread),而attachApplication(mAppThread)又接着调用了attachApplicationLocked(thread, callingPid)

 // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }

如果此时是正常模式,那就执行mStackSupervisor.attachApplicationLocked(app),坚持住,曙光马上就要来临了!

 ActivityRecord hr = stack.topRunningActivityLocked(null);
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                            Slog.w(TAG, "Exception in new application when starting activity "
                                  + hr.intent.getComponent().flattenToShortString(), e);
                            throw e;
                        }
                    }
                }

还是和之前的流程一样,在ActivityStackSupervisor中,先获取处于栈顶的ActivityStack,再通过ActivityStack获取处于顶层ActivityRecord的,最后通过realStartActivityLocked(hr, app, true, true),顾名思义,真正的启动一个Activity。

不过有眼尖的朋友发现了,这里的if判断怎么有个条件是hr.app == null呢?要知道,虽然之前的代码中ActivityThread已经创建好了,但此时还没有与ActivityRecord关联起来,而这正是realStartActivityLocked()要做的。

那么Activity要如何真正的启动呢?
第一步 mWindowManager.setAppVisibility(r.appToken, true);,将该Activity所对应的window设置成可见。
第二步,调用app.thread.scheduleLaunchActivity(...参数很长...)方法

ActivityClientRecord r = new ActivityClientRecord();
            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);

scheduleLaunchActivity()中先将参数封装成ActivityClientRecord对象,再向Handler H发送Message。这部分流程和之前说的pause流程相似,会调用 handleLaunchActivity(r, null),接着调用performLaunchActivity(r, customIntent),再通过反射将Activity创建出来,最后开始Activity生命周期的调用。

 Activity activity = null;
        try {
            ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } 

总结

看到这里,正常人应该已经晕的不要不要的了,所以还是需要象征性的总结一下。

(一)ActivityManagerService(ActivityManagerNative)是核心管理类,负责组件的管理,在这里主要与ActivityStackSupervisor通信。
(二)ActivityStackSupervisor管理整个手机任务栈,即管理着ActivityStack。
(三)ActivityStack是Activity的栈,即任务栈,从中可以获取需要进行操作的ActivityRecord,并且可以对任务的进程进行操作。
(四)ActivityThread是安卓java应用层的入口函数类,它会执行具体对Activity的操作,并将结果通知给ActivityManagerService。

完结撒花~

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

推荐阅读更多精彩内容