ActivityManagerService-C 管理场景分析

版权说明:本文为 开开向前冲 记录文章,转载请注明出处;
注:限于作者水平有限,文中有不对的地方还请指教

在文章ActivityManagerService-A中对AMS 中核心的几个数据类都有说道,在ActivityManagerService-B中对ActivityRecord 的状态变化做了一个介绍,Activity的状态只是管理的结果,具体的过程需要需要详细分析;本文将从几个简单的场景结合代码来分析Activity的管理过程;

场景1:Activity在AMS中的存在形式是ActivityRecord,应用程序的组件是Activity,ActivityRecord与Activity是如何关联的呢?——>static class Token extends IApplicationToken.Stub

这里涉及到到应用程序的主线程-ActivityThread(ActivityThread其实并不是一个线程类),这个类已经有很多人讲解,这里推荐一篇关于ActivityThread和ApplicationThread的解析,ActivityThread有一个内部类 ApplicationThread;
class ApplicationThread extends ApplicationThreadNative
class ApplicationThreadNative extends Binder implements IApplicationThread
AMS 和ActivityStackSupervisor中的 app.thread 就是这里ActivityThread的内部类ApplicationThread对象;

Activity在ActivityThread中的存在形式是ActivityClientRecord;ActivityRecord有一个IApplicationToken.Stub类型的变量appToken,在构造ActivityRecord的时候被初始化;

------>frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java
  static class Token extends IApplicationToken.Stub {
        final WeakReference<ActivityRecord> weakActivity;
        Token(ActivityRecord activity) {
            weakActivity = new WeakReference<ActivityRecord>(activity);
        }
        ......
------>frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java
    ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
            int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
            ActivityInfo aInfo, Configuration _configuration,
            ActivityRecord _resultTo, String _resultWho, int _reqCode,
            boolean _componentSpecified, ActivityStackSupervisor supervisor,
            ActivityContainer container, Bundle options) {
          service = _service;
          appToken = new Token(this);
          ......
    }

构造一个ActivityRecord时,会将自己(this)传给Token,变量ActivityRecord.appToken存的就是最终创建出来的Token。

上述ActivityRecord.appToken的功能是什么呢?ActivityRecord是在Activity的启动过程中被初始化,继续跟进Activity的启动过程;

------>frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
------>frameworks\base\core\java\android\app\ActivityThread.java
ActivityStackSupervisor.realStartActivityLocked(ActivityRecord, ...)
  ——> IApplicationThread.scheduleLaunchActivity(...,token, ...)
      ——>跨进程调用,调用ActivityThread.ApplicationThread.scheduleLaunchActivity(...,token,...)
      ——>ApplicationThread.scheduleLaunchActivity(...token, ...)
        ——>ActivityThread.handleLaunchActivity(LAUNCH_ACTIVITY)
          ——>ActivityThread.performLaunchActivity(ActivityClientRecord, ...)
               ——>从ActivityActivityClientRecord取出token使用
               ——> Activity.attch(...token, ...) //attach中将token 赋值给Activity的mToken变量

通过上述函数调用,ActivityRecord的appToken就和应用进程的mToken建立关联;在发生Activity切换时,应用进程会将上一个Activity的Token(AMS.startActivity()的输入参数IBinder resultTo)传递给系统进程,系统进程会根据这个Token找到ActivityRecord,对其完成调度后,再通知应用进程:Activity状态发生了变化。

场景2:启动新Activity时,需要将新ActivityRecord压入任务栈顶;

这里先用自然语言描述一下可能的情况:1:新的ActivityRecord 的任务栈已经存在,处于后台,此时需要将这个后台任务栈切换为前台任务栈,然后将这个ActivityRecord置于前台任务栈栈顶; 2:新ActivityRecord所在的任务栈没有,需要新建任务栈,然后将ActivityRecord置于栈顶;

代码逻辑:任务栈存在于后台的情况:

第一步:需要先找到ActivityRecord所在的任务(TaskRecord);
第二步:将TaskRecord所在的ActivityStack移动到前台;
第三步:将TaskRecord移动到ActivityStack的栈顶;

1:找到ActivityRecord所在的TaskRecord: findTaskLocked()
------>frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
/**
 * Returns the top activity in any existing task matching the given
 * Intent.  Returns null if no such task is found.
 */
ActivityRecord findTaskLocked(ActivityRecord target) {
    Intent intent = target.intent;
    ActivityInfo info = target.info;
    ComponentName cls = intent.getComponent();
    if (info.targetActivity != null) {
        cls = new ComponentName(info.packageName, info.targetActivity);
    }
    ...
    for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
        final TaskRecord task = mTaskHistory.get(taskNdx);
        ...
        final ActivityRecord r = task.getTopActivity();
            if (r == null || r.finishing || r.userId != userId ||
                    r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {//这些条件的直接过滤
                continue;
            }
        ...
        final Intent taskIntent = task.intent;
        final Intent affinityIntent = task.affinityIntent;
        ...
        if (!isDocument && !taskIsDocument && task.rootAffinity != null) {
            if (task.rootAffinity.equals(target.taskAffinity)) { //Affinity和Task的rootAffinity相同,
            //则就是这个task 了;
                return r;
            }
        } else if (taskIntent != null && taskIntent.getComponent() != null &&
            taskIntent.getComponent().compareTo(cls) == 0 &&
            Objects.equals(documentData, taskDocumentData)) {//Task的Intent 和target的包名相同
            return r;
        } else if if (affinityIntent != null && affinityIntent.getComponent() != null &&
            affinityIntent.getComponent().compareTo(cls) == 0 &&
            Objects.equals(documentData, taskDocumentData)) { //Affinity Intent的包名相同
            return r
        }
        ...
    }
    return null;
}

改函数主要是对ActivityStack中的所有Task进行遍历,找到ActivityRecord的所在的Task,如果找到Task,则返回Task最上面的ActivityRecord;找到Task的条件如下:

  • ActivityRecord target 的affinity和正在遍历的Task的rootAffinity相同;
  • ActivityRecord target的包名和Task的intent 的包名相同;
  • ActivityRecord target的包名和Task的affinityIntent的包名相同;
2:将TaskRecord所在的ActivityStack调整到前台
  • 2.1:findTaskLocked()方法找到Activity所在的Task的顶部ActivityRecord,根据这个ActivityRecord找到所在的TaskRecord和ActivityStack;
------> frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java
    final int startActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean doResume, Bundle options, TaskRecord inTask) {
                ......
                ActivityRecord intentActivity = !launchSingleInstance ?
                        findTaskLocked(r) : findActivityLocked(intent, r.info);
                if (intentActivity != null) {
                    ......
                    if (r.task == null) {
                        r.task = intentActivity.task;
                    }
                    targetStack = intentActivity.task.stack;//找到ActivityRecord r所在的ActivityStack
                    targetStack.mLastPausedActivity = null;
                    targetStack.moveToFront("intentActivityFound");
                  ......
  • 2.2:调整ActivityStack,主要根据ActivityStack的类型来进行ActivityStack栈(mStacks)调整;moveToFront();
------>frameworks\base\services\core\java\com\android\server\am\ActivityStack.java
final void moveToFront(String reason) {
        if (isAttached()) {//是否绑定到显示设备
            if (isOnHomeDisplay()) {//是否显示在默认显示设备
                mStackSupervisor.moveHomeStack(isHomeStack(), reason);//ActivityStack栈调整
            }
            mStacks.remove(this);//删除mStacks栈中的该ActivityStack
            mStacks.add(this);//将该ActivityStack添加到mStacks中
            final TaskRecord task = topTask();
            if (task != null) {
                mWindowManager.moveTaskToTop(task.taskId);
            }
        }
    }

final boolean isHomeStack() {//判断当前ActivityStack 是不是HomeStack
        return mStackId == HOME_STACK_ID;
    }

void moveHomeStack(boolean toFront, String reason) {//栈调整
    // 获取当前的Top Stack
    ArrayList<ActivityStack> stacks = mHomeStack.mStacks;//mStacks代表的是绑定到显示设备的所有Stack;
                          //所以这里也可以用mFocusedStack.mStacks
    final int topNdx = stacks.size() - 1;
    if (topNdx <= 0) {
        return;
    }
    ActivityStack topStack = stacks.get(topNdx);
    // HomeStack是否需要调整
    final boolean homeInFront = topStack == mHomeStack;//判断stacks 栈顶的ActivityStack是不是HomeStack,
    // 即判断当前HomeStack是不是在前台;
    if (homeInFront != toFront) {
        mLastFocusedStack = topStack;
        stacks.remove(mHomeStack);
        stacks.add(toFront ? topNdx : 0, mHomeStack);//这里根据toFront来调整HomeStack位置
        mFocusedStack = stacks.get(topNdx);//调整mFocusedStack
    }
    ...
    if (mService.mBooting || !mService.mBooted) {  // 开机过程处理
        final ActivityRecord r = topRunningActivityLocked();
        if (r != null && r.idle) {
            checkFinishBootingLocked();
        }
    }
}

moveHomeStack()方法根据mStacks栈顶的ActivityStack是否是HomeStack来对HomeStack和mFocusedStack 进行调整;homeInFront 表示mStacks栈顶是不是HomeStack(即HomeStack 是否在前台);toFront 是isHomeStack()的返回值,即当前ActivityStack是否是HomeStack,也就是需要将当前ActivityStack调整到前台还是后台;

这里存在如下4种情况:

  • homeInFront = true,即topStack == mHomeStack, 表示当前mStacks最顶的栈是HomeStack,即表示HomeStack在前台;toFront = false 即isHomeStack()返回false,即表示ActivityRecord所在的栈不是HomeStack,所以需要将HomeStack调整到mStacks的底部,即将HomeStack调整到mStacks的0号位置;
  • homeInFront = true, toFront = true: 表示HomeStack在前台,要显示的ActivityStack就是HomeStack,所以不需要对mStacks进行调整;
  • homeInFront = false, toFront = true: 表示HomeStack在后台,要显示的ActivityStack就是HomeStack,需要将HomeStack调整到mStacks的最顶端;
  • homeInFront = false, toFront = false: 表示HomeStack在后台,要显示的ActivityStack 也 是HomeStack,不需要对mStacks进行调整。

moveToFront中moveHomeStack()调整完HomeStack位置后,调整mStacks栈中当前ActivityStack位置,即先删除当前ActivityStack,然后再向mStacks中添加当前ActivityStack(这样做时为了保证将当前ActivityStack置于栈顶);

3:将当前需要显示的ActivityRecord的TaskRecord 移动到ActivityStack 的栈顶;moveTaskToFrontLocked()

ActivityStack有前台和后台之分,前台ActivityStack代表当前显示的ActivityRecord在的TaskRecord所在的ActivityStack(不是前台ActivityStack中所有的TaskRecord 和ActivityRecord都处于可见,只有ActivityStack 栈顶的TaskRecord 栈顶的ActivityRecord处于可见状态);

第二步已经将ActivityRecord所在的Task的Stack移动到前台,现在需要将需要显示的ActivityRecord 所在的TaskRecord 移动到ActivityStack的栈顶,以及将ActivityRecord移动到TaskRecord的栈顶;

------> frameworks\base\services\core\java\com\android\server\am\ActivityStack.java
final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord source, Bundle options,
            String reason) {
        final int numTasks = mTaskHistory.size();
        final int index = mTaskHistory.indexOf(tr);
        if (numTasks == 0 || index < 0)  { //numTasks 代表当前ActivityStack中的所有TaskRecord数量,
        //index 表示当前需要调整的TaskRecord在ActivityStack mTaskHistory中的位置
            // nothing to do!
            if (source != null &&
                    (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
                ActivityOptions.abort(options);
            } else {
                updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
            }
            return;
        }

        // Shift all activities with this task up to the top
        // of the stack, keeping them in the same internal order.
        insertTaskAtTop(tr);//将Task置于ActivityStack栈顶
        moveToFront(reason);//前面说过moveToFront()方法,前面的情况只调用了moveToFront(),
        //不会调用moveTaskToFrontLocked(); 
        //一旦要将任务调整到ActivityStack栈顶,意味着ActivityStack也一定要调整到前台;

        if (source != null &&
                (source.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
            mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
            ActivityRecord r = topRunningActivityLocked(null);
            if (r != null) {
                mNoAnimActivities.add(r);
            }
            ActivityOptions.abort(options);
        } else {
            updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
        }

        mStackSupervisor.resumeTopActivitiesLocked();//当Activity各种栈调整好后,就可以将当前Activity的状态置为RESUMED
        ......
    }

private void insertTaskAtTop(TaskRecord task) {
        // If this is being moved to the top by another activity or being launched from the home
        // activity, set mOnTopOfHome accordingly.
        if (isOnHomeDisplay()) {
            ActivityStack lastStack = mStackSupervisor.getLastStack();
            final boolean fromHome = lastStack.isHomeStack();
            if (!isHomeStack() && (fromHome || topTask() != task)) {
                task.setTaskToReturnTo(fromHome
                        ? lastStack.topTask() == null
                                ? HOME_ACTIVITY_TYPE
                                : lastStack.topTask().taskType
                        : APPLICATION_ACTIVITY_TYPE);//设置ReturnTo Task类型
            }
        } else {
            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
        }
        mTaskHistory.remove(task);//先从mTaskHistory中移除task
        // Now put task at top.
        int taskNdx = mTaskHistory.size();
        if (!isCurrentProfileLocked(task.userId)) {
            // Put non-current user tasks below current user tasks.
            while (--taskNdx >= 0) {
                if (!isCurrentProfileLocked(mTaskHistory.get(taskNdx).userId)) {
                    break;
                }
            }
            ++taskNdx;
        }
        mTaskHistory.add(taskNdx, task);//将task添加到mTaskHistory,确保添加到了顶部
        updateTaskMovement(task, true);
    }

这里将ActivityRecord所在的TaskRecord 移动到ActivityStack的栈顶,还需要将需要显示的ActivityRecord移动到TaskRecord的栈顶,这个过程通过addActivityToTop()来实现;addActivityToTop()在ActivityStack.java的startActivityLocked()方法中被调用,startActivityLocked()在ActivityStackSupervisor.java的startActivityUncheckedLocked()方法中被调用;

------> frameworks\base\services\core\java\com\android\server\am\TaskRecord.java
    void addActivityToTop(ActivityRecord r) {
        addActivityAtIndex(mActivities.size(), r);
    }

    void addActivityAtIndex(int index, ActivityRecord r) {
        // Remove r first, and if it wasn't already in the list and it's fullscreen, count it.
        if (!mActivities.remove(r) && r.fullscreen) {//删除mActivities中已经存在的r
            // Was not previously in list.
            numFullscreen++;
        }
        // Only set this based on the first activity
        if (mActivities.isEmpty()) { //判断当前启动的Activity是否为该任务Task的第一个Activity
            taskType = r.mActivityType;
            isPersistable = r.isPersistable();
            mCallingUid = r.launchedFromUid;
            mCallingPackage = r.launchedFromPackage;
            // Clamp to [1, max].
            maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
                    ActivityManager.getMaxAppRecentsLimitStatic());
        } else {
            // Otherwise make all added activities match this one.
            r.mActivityType = taskType;
        }
        Log.d(TAG+"mActivities.add",Log.getStackTraceString(new Throwable()));//Keiven-chen
        mActivities.add(index, r);//指定将当前Activity添加到mActivities中的位置
        updateEffectiveIntent();//如函数名,更新任务栈相关Intent
        if (r.isPersistable()) {
            mService.notifyTaskPersisterLocked(this, false);
        }
    }

这里需要明确一点问题,mActivities就是我说的TaskRecord栈, mTaskHistory也就是ActivityStack栈;

场景3: 任务栈会经常调整,任务栈刚开始从底到顶是 A - B - C, 经过调整后任务栈变化为 C - B - A,这个过程会对整个任务栈带来哪些改变呢?

该问题看似只需要调整mActivities的顺序,但是会这么简单吗?ActivityManagerService-A文中有说过TaskRecord类,随着mActivities栈的改变,TaskRecord中的属性是否也应该改变呢?比如Affinity,Intent;这些值是如何改变的呢?这里涉及updateEffectiveIntent()和setFrontOfTask();

每次调整完mActivities后,需要调用updateEffectiveIntent方法来修改该TaskRecord相关的Intent;

void updateEffectiveIntent() {
    final int effectiveRootIndex = findEffectiveRootIndex();//找到根Activity索引
    final ActivityRecord r = mActivities.get(effectiveRootIndex);
    setIntent(r);//设置Intent相关属性,顺着代码往下看,
}
    /** Sets the original intent, and the calling uid and package. */
    void setIntent(ActivityRecord r) {
        setIntent(r.intent, r.info);
        mCallingUid = r.launchedFromUid;
        mCallingPackage = r.launchedFromPackage;
    }

    /** Sets the original intent, _without_ updating the calling uid or package. */
    private void setIntent(Intent _intent, ActivityInfo info) { //该重载方法更新affinity, 
    //origActivity ,realActivity等属性;
        if (intent == null) {
            mNeverRelinquishIdentity =
                    (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
        } else if (mNeverRelinquishIdentity) {
            return;
        }

        affinity = info.taskAffinity;
        if (intent == null) {
            // If this task already has an intent associated with it, don't set the root
            // affinity -- we don't want it changing after initially set, but the initially
            // set value may be null.
            rootAffinity = affinity;
        }
        effectiveUid = info.applicationInfo.uid;
        stringName = null;

        if (info.targetActivity == null) {
            if (_intent != null) {
                // If this Intent has a selector, we want to clear it for the
                // recent task since it is not relevant if the user later wants
                // to re-launch the app.
                if (_intent.getSelector() != null || _intent.getSourceBounds() != null) {
                    _intent = new Intent(_intent);
                    _intent.setSelector(null);
                    _intent.setSourceBounds(null);
                }
            }
            if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
                    "Setting Intent of " + this + " to " + _intent);
            intent = _intent;
            realActivity = _intent != null ? _intent.getComponent() : null;
            origActivity = null;
        } else {
            ComponentName targetComponent = new ComponentName(
                    info.packageName, info.targetActivity);
            if (_intent != null) {
                Intent targetIntent = new Intent(_intent);
                targetIntent.setComponent(targetComponent);
                targetIntent.setSelector(null);
                targetIntent.setSourceBounds(null);
                if (ActivityManagerService.DEBUG_TASKS) Slog.v(ActivityManagerService.TAG,
                        "Setting Intent of " + this + " to target " + targetIntent);
                intent = targetIntent;
                realActivity = targetComponent;
                origActivity = _intent.getComponent();
            } else {
                intent = null;
                realActivity = targetComponent;
                origActivity = new ComponentName(info.packageName, info.name);
            }
        }

        final int intentFlags = intent == null ? 0 : intent.getFlags();
        if ((intentFlags & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
            // Once we are set to an Intent with this flag, we count this
            // task as having a true root activity.
            rootWasReset = true;
        }

        userId = UserHandle.getUserId(info.applicationInfo.uid);
        if ((info.flags & ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS) != 0) {
            // If the activity itself has requested auto-remove, then just always do it.
            autoRemoveRecents = true;
        } else if ((intentFlags & (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
                | Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS)) == Intent.FLAG_ACTIVITY_NEW_DOCUMENT) {
            // If the caller has not asked for the document to be retained, then we may
            // want to turn on auto-remove, depending on whether the target has set its
            // own document launch mode.
            if (info.documentLaunchMode != ActivityInfo.DOCUMENT_LAUNCH_NONE) {
                autoRemoveRecents = false;
            } else {
                autoRemoveRecents = true;
            }
        } else {
            autoRemoveRecents = false;
        }
    }

任务栈A - B - C的栈底是 A,调整后, C - B - A的栈底是C,然而,TaskRecord并没有标注当前栈底的属性, 这个栈底是根据任务栈中每个ActivityRecord的frontOfTask属性来标识:这个属性通过setFrontOfTask()方法来修改;

/** Call after activity movement or finish to make sure that frontOfTask is set correctly */
    final void setFrontOfTask() {
        boolean foundFront = false;
        final int numActivities = mActivities.size();
        for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {//重栈底开始
            final ActivityRecord r = mActivities.get(activityNdx);
            if (foundFront || r.finishing) {
                r.frontOfTask = false; //非栈底Activity 该属性都置为false
            } else {
                r.frontOfTask = true;//栈底Activity 该属性置为true
                // Set frontOfTask false for every following activity.
                foundFront = true;
            }
        }
        if (!foundFront && numActivities > 0) {
            // All activities of this task are finishing. As we ought to have a frontOfTask
            // activity, make the bottom activity front.
            mActivities.get(0).frontOfTask = true; //这种情况将mActivities第一个Activity的frontOfTask置为true;
        }
    }

该函数对任务栈从底到顶进行遍历,找到第一个未结束(finishing = false)的ActivityRecord, 将其frontOfTask属性设置成true;其他所有ActivtyRecord的frontOfTask属性设置为false。这样就能标记该ActivityRecord为所在任务栈的栈底;

参考文章:Android四大组件之Activity--管理方式

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

推荐阅读更多精彩内容