Intent Flags VS launchMode

??本文試圖說明 Activity launchMode 原理,各種常用的 Intent Flag 原理 读第,以及l(fā)aunchMode 和 Intent Flag 的區(qū)別胶哲,相似點等昧诱;以及有可能的使用場景(源碼基于 7.0 分析)晓淀。

SingleTask VS Intent.FLAG_NEW_TASK

??Activity 中有四大啟動模式,較常用的一種為SingleTask官方注釋如下:

"singleTask" can only begin a task. They are always at the root of 
the activity stack. Moreover, the device can hold only one instance of 
the activity at a time — only one such task.

??大致翻譯如下:如果一個Activity設置為SingleTask盏档,那么它必須在當前棧的底部凶掰,并且在當前棧中,只能有一個該Activity的實例蜈亩。
??上述說法其實是錯誤的懦窘,一個被SingleTask標識的Activity是可以不在棧底的。并且在一個棧中稚配,只能有一個該Activity實例畅涂。那么 SingleTask 的原理是什么呢。
??不同應用的Activity以及Activity的跳轉(zhuǎn)是由AMS來完成的,當啟動一個Activity時糕非,最后都是由 AMS 來完成嘿辟,最后會調(diào)用到 ActivityStarter的startActivityUnchecked()方法,該方法代碼如下:

  // Note: This method should only be called from {@link startActivity}.
    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
                                       IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
                                       int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
                                       ActivityRecord[] outActivity) {
           //  1.1 解析 LaunchMode , 設置初始化狀態(tài)
           setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                voiceInteractor);
           // 1.2 實現(xiàn) LaunchMode 到 LaunchFlag 轉(zhuǎn)換操作
           computeLaunchingTaskFlags();
           ...
           // 1.3 此處為關鍵點臊岸,尋找可重用的 ActivityRecord實例
           ActivityRecord reusedActivity = getReusableIntentActivity();
           ...
           // 1.4 如果 reusedActivity 不為 null ,走相關邏輯
           if(reusedActivity != null){
                 //后續(xù)具體分析..
           }
           ...
     }               

??應用在真正啟動一個 Activity 之前,會先走上述1.1~1.3的初始化過程尊流,下面分別來解釋一下 1.1 ~1.3具體做了哪些工作帅戒。

1.1:setInitialState() 初始化工作

在 setInitialState() 過程中,會首先做一些初始化的操作崖技,如記錄該 ActivityRecord 的啟動模式逻住,activityResult 校驗等等,代碼如下:

private void setInitialState(ActivityRecord r, ActivityOptions options, TaskRecord inTask,
            boolean doResume, int startFlags, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor) {
        reset();

        mStartActivity = r;
        mIntent = r.intent;
        mOptions = options;
        mCallingUid = r.launchedFromUid;
        mSourceRecord = sourceRecord;
        mVoiceSession = voiceSession;
        mVoiceInteractor = voiceInteractor;

        mLaunchBounds = getOverrideBounds(r, options, inTask);
        // 常見的啟動模式的標識迎献,賦值給成員變量
        // 標識將要啟動的 Activity 的 LaunchMode
        mLaunchSingleTop = r.launchMode == LAUNCH_SINGLE_TOP;
        mLaunchSingleInstance = r.launchMode == LAUNCH_SINGLE_INSTANCE;
        mLaunchSingleTask = r.launchMode == LAUNCH_SINGLE_TASK;
        mLaunchFlags = adjustLaunchFlagsToDocumentMode(
                r, mLaunchSingleInstance, mLaunchSingleTask, mIntent.getFlags());
        mLaunchTaskBehind = r.mLaunchTaskBehind
                && !mLaunchSingleTask && !mLaunchSingleInstance
                && (mLaunchFlags & FLAG_ACTIVITY_NEW_DOCUMENT) != 0;
         
        // 此處也是一個重要方法鄙信,與 startActivityForResult() 密切相關
        sendNewTaskResultRequestIfNeeded();
        
        ...
    }

??從上述初始化操作可以看出,將目標Activity的Launch Mode 賦值給相關的成員變量忿晕,并且在之后有一步 sendNewTaskResultRequestIfNeeded() 的操作装诡,那么這個操作是干什么的呢?該方法代碼如下:

private void sendNewTaskResultRequestIfNeeded() {
        if (mStartActivity.resultTo != null && (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0
                && mStartActivity.resultTo.task.stack != null) {
            // For whatever reason this activity is being launched into a new task...
            // yet the caller has requested a result back.  Well, that is pretty messed up,
            // so instead immediately send back a cancel and let the new task continue launched
            // as normal without a dependency on its originator.
            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
            mStartActivity.resultTo.task.stack.sendActivityResultLocked(-1, mStartActivity.resultTo,
                    mStartActivity.resultWho, mStartActivity.requestCode, RESULT_CANCELED, null);
            mStartActivity.resultTo = null;
        }
    }

??從該方法注釋可以看出践盼,如果以startActivityForResult() 方式啟動(RequestCode > 0),并且Intent Flag 有設置 FLAG_ACTIVITY_NEW_TASK 標識鸦采,那么當前 Activity 會立即收到 CANCEL 回調(diào)。
??上述問題也是一個比較常見的問題咕幻,從代碼可以看出渔伯,如果想要正確的收到 onActivityResult() 回調(diào),那么在啟動 Activity 的 Intent 中肄程,一定不能加 FLAG_ACTIVITY_NEW_TASK 標識锣吼。
??分析完上述 1.1 之后选浑,接著來看 1.2 computeLaunchingTaskFlags() 具體做了什么事情。

1.2 : computeLaunchingTaskFlags()

代碼如下:

private void computeLaunchingTaskFlags() {
        // If the caller is not coming from another activity, but has given us an explicit task into
        // which they would like us to launch the new activity, then let's see about doing that.
        // 此處不在本文的分析范圍內(nèi)玄叠,如果用戶是通過 Application Context 或者 
        // Service 啟動的 目標 Activity 古徒,并且有指定要宿主到哪個 task
        // 該判斷條件才會成立。
        if (mSourceRecord == null && mInTask != null && mInTask.stack != null) {
            final Intent baseIntent = mInTask.getBaseIntent();
            final ActivityRecord root = mInTask.getRootActivity();
            if (baseIntent == null) {
                ActivityOptions.abort(mOptions);
                throw new IllegalArgumentException("Launching into task without base intent: "
                        + mInTask);
            }

            // If this task is empty, then we are adding the first activity -- it
            // determines the root, and must be launching as a NEW_TASK.
            if (mLaunchSingleInstance || mLaunchSingleTask) {
                if (!baseIntent.getComponent().equals(mStartActivity.intent.getComponent())) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Trying to launch singleInstance/Task "
                            + mStartActivity + " into different task " + mInTask);
                }
                if (root != null) {
                    ActivityOptions.abort(mOptions);
                    throw new IllegalArgumentException("Caller with mInTask " + mInTask
                            + " has root " + root + " but target is singleInstance/Task");
                }
            }

            // If task is empty, then adopt the interesting intent launch flags in to the
            // activity being started.
            if (root == null) {
                final int flagsOfInterest = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
                        | FLAG_ACTIVITY_NEW_DOCUMENT | FLAG_ACTIVITY_RETAIN_IN_RECENTS;
                mLaunchFlags = (mLaunchFlags & ~flagsOfInterest)
                        | (baseIntent.getFlags() & flagsOfInterest);
                mIntent.setFlags(mLaunchFlags);
                mInTask.setIntent(mStartActivity);
                mAddingToTask = true;

                // If the task is not empty and the caller is asking to start it as the root of
                // a new task, then we don't actually want to start this on the task. We will
                // bring the task to the front, and possibly give it a new intent.
            } else if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
                mAddingToTask = false;

            } else {
                mAddingToTask = true;
            }

            mReuseTask = mInTask;
        } else {
            mInTask = null;
            // Launch ResolverActivity in the source task, so that it stays in the task bounds
            // when in freeform workspace.
            // Also put noDisplay activities in the source task. These by itself can be placed
            // in any task/stack, however it could launch other activities like ResolverActivity,
            // and we want those to stay in the original task.
            if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
                    && mSourceRecord.isFreeform())  {
                mAddingToTask = true;
            }
        }

        // 重點分析此處 读恃, 如果啟動目標 Activity 為 Activity Context ,那么會為 mSourceRecord 
        // 賦值,如果是從非 Activity 啟動隧膘,比如 Application Context 等,那么mSourceRecord
        // 為 null 寺惫。
        if (mInTask == null) {
            if (mSourceRecord == null) {
                // This activity is not being started from another...  in this
                // case we -always- start a new task.
                // 如果是從非 Activity 入口過來疹吃,強制加上 FLAG_NEW_TASK 標識。
                if ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) == 0 && mInTask == null) {
                    Slog.w(TAG, "startActivity called from non-Activity context; forcing " +
                            "Intent.FLAG_ACTIVITY_NEW_TASK for: " + mIntent);
                    mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
                }
            } else if (mSourceRecord.launchMode == LAUNCH_SINGLE_INSTANCE) {
                // The original activity who is starting us is running as a single
                // instance...  this new activity it is starting must go on its
                // own task.
                // 如果 當前 Activity Launch Mode 為 singleInstance西雀,代表不允許目標
                // Activity 與宿主 Activity 棧實例共用一個 task 棧萨驶,所以也會強制加上
                // FLAG_ACTIVITY_NEW_TASK 標識。
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            } else if (mLaunchSingleInstance || mLaunchSingleTask) {
                // The activity being started is a single instance...  it always
                // gets launched into its own task.
                // 如果目標 Activity 的 LaunchMode 為 singleTask 或者 singleInstance 
                // 那么也強制加上 FLAG_ACTIVITY_NEW_TASK 標識艇肴。
                mLaunchFlags |= FLAG_ACTIVITY_NEW_TASK;
            }
        }
    }

??該方法的說明已經(jīng)在上述代碼中注釋的比較清楚腔呜,總結一些,可以羅列為以下幾點:

  • 如果是從非 Activity 入口過來豆挽,比如 Application Context 育谬、Service 等 , 強制加上 FLAG_ACTIVITY_NEW_TASK 標識券盅。
  • 如果目標 Activity 啟動模式為 SingleTask 或者 SingleInstance 帮哈,那么也強制加上 FLAG_ACTIVITY_NEW_TASK 標識。
  • 如果源 Activity 啟動模式為 SingleInstance , 那么肯定不允許目標 Activity 與源 Activity 公用一個 Task 棧锰镀,所以也強制加上 FLAG_ACTIVITY_NEW_TASK 標識娘侍。
1.3 :尋找可重用的 ActivityRecord 實例

getReusableIntentActivity() 方法代碼如下:

/**
     * Decide whether the new activity should be inserted into an existing task. Returns null
     * if not or an ActivityRecord with the task into which the new activity should be added.
     */
    private ActivityRecord getReusableIntentActivity() {
        // We may want to try to place the new activity in to an existing task.  We always
        // do this if the target activity is singleTask or singleInstance; we will also do
        // this if NEW_TASK has been requested, and there is not an additional qualifier telling
        // us to still place it in a new task: multi task, always doc mode, or being asked to
        // launch this as a new task behind the current one.
        boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
                (mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
                || mLaunchSingleInstance || mLaunchSingleTask;
        // If bring to front is requested, and no result is requested and we have not been given
        // an explicit task to launch in to, and we can find a task that was started with this
        // same component, then instead of launching bring that one to the front.
        putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
        ActivityRecord intentActivity = null;
        if (mOptions != null && mOptions.getLaunchTaskId() != -1) {
            final TaskRecord task = mSupervisor.anyTaskForIdLocked(mOptions.getLaunchTaskId());
            intentActivity = task != null ? task.getTopActivity() : null;
        } else if (putIntoExistingTask) {
            if (mLaunchSingleInstance) {
                // There can be one and only one instance of single instance activity in the
                // history, and it is always in its own unique task, so we do a special search.
               intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info, false);
            } else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
                // For the launch adjacent case we only want to put the activity in an existing
                // task if the activity already exists in the history.
                intentActivity = mSupervisor.findActivityLocked(mIntent, mStartActivity.info,
                        !mLaunchSingleTask);
            } else {
                // Otherwise find the best task to put the activity in.
                intentActivity = mSupervisor.findTaskLocked(mStartActivity);
            }
        }
        return intentActivity;
    }

??從上述代碼注釋可以看出,該方法主要作用為判斷目標 Activity 是否要插入到一個已經(jīng)存在的棧中泳炉,主要判斷依據(jù)羅列如下:

  • 對于 LaunchMode 為 singleTask 或 singleInstance 的 Activity憾筏,強制嘗試去尋找是否已經(jīng)存在一個已經(jīng)存在的 Task。
  • 對于 含有 FLAG_ACTIVITY_NEW_TASK 標識的 Intent花鹅,也強制嘗試去尋找氧腰。
  • 若目標 Activity LaunchMode 為 singleInstance ,強制去找是否已經(jīng)存在相同的 ActivityRecord 實例刨肃。
  • 若LaunchMode 不為 singleInstance古拴,則強制去尋找已經(jīng)存在的 Task 實例,特別說明的是真友,此處尋找是否存在相同的 task 實例是通過判斷 taskAffinity 來完成的黄痪,具體代碼邏輯在 mSupervisor.findTaskLocked(mStartActivity) 方法中,此處不再詳細說明盔然。

??通過上述方法可以看出桅打,如果一個 Activity 的 LaunchMode 被標識為 singleInstance , 那么它在 AMS 中永遠只存在唯一的一份實例是嗜。對于 singleTask 或 FLAG_ACTIVITY_NEW_TASK , 都嘗試先根據(jù)該 Activity 對應的 taskAffinity 去找是否已經(jīng)存在相關的棧,如果存在挺尾,則將目標 Activity 放入該棧中鹅搪。
??從上述1.1 ~ 1.3 的分析,發(fā)現(xiàn) launchMode 為 singleTask 與 Intent FLAG_ACTIVITY_NEW_TASK 并沒有什么區(qū)別潦嘶,singleTask 在 1.2 computeLaunchingTaskFlags() 方法中也會強制加上 FLAG_ACTIVITY_NEW_TASK 標識涩嚣,那么兩者的區(qū)別到底是什么呢?

1.4 : reusedActivity 不為 null 處理

從1.3 處分析可知掂僵,對于 launchMode 為 singleTask 或者 Intent Flag 為 FLAG_ACTIVITY_NEW_TASK 的標識航厚,都會首先嘗試去找一個已經(jīng)存在的棧,此處如果可以找到锰蓬,則將棧中的一個 ActivityRecord 實例賦值給 reusedActivity 幔睬,然后繼續(xù)走接下來的流程,代碼如下:

if (reusedActivity != null) {
            ...
            // 重點分析此處方法
            setTaskFromIntentActivity(reusedActivity);
            if (!mAddingToTask && mReuseTask == null) {
                // We didn't do anything...  but it was needed (a.k.a., client don't use that
                // intent!)  And for paranoia, make sure we have correctly resumed the top activity.
                resumeTargetStackIfNeeded();
                if (outActivity != null && outActivity.length > 0) {
                    outActivity[0] = reusedActivity;
                }
                return START_TASK_TO_FRONT;
            }
        }

??重點分析 setTaskFromIntentActivity(reusedActivity) 方法芹扭,其中 reusedActivity 代表 1.3 找到的 ActivityRecord 實例麻顶, 該方法代碼如下:

private void setTaskFromIntentActivity(ActivityRecord intentActivity) {
        if ((mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK)) {
            // The caller has requested to completely replace any existing task with its new
            // activity. Well that should not be too hard...
            // 如果 Intent flag 同時設置有 FLAG_ACTIVITY_CLEAR_TASK 與 FLAG_ACTIVITY_NEW_TASK
            // 那么會把當前的棧清空,重新開啟一個棧
            mReuseTask = intentActivity.task;
            // 清空棧操作
            mReuseTask.performClearTaskLocked();
            mReuseTask.setIntent(mStartActivity);
            // When we clear the task - focus will be adjusted, which will bring another task
            // to top before we launch the activity we need. This will temporary swap their
            // mTaskToReturnTo values and we don't want to overwrite them accidentally.
            mMovedOtherTask = true;
        } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) != 0
                || mLaunchSingleInstance || mLaunchSingleTask) {
            // 如果目標 Activity launchMode 為 singleTask或
            // 者singleInstance , 或者 Intent flag 含有 FLAG_CLEAR_TOP 標識 舱卡,那么首先
            // 去清除 棧里面的元素辅肾,直到找到可重用的 ActivityRecord 為止。
           // 此處是一個關鍵點
            ActivityRecord top = intentActivity.task.performClearTaskLocked(mStartActivity,
                    mLaunchFlags);
            if (top == null) {
                // A special case: we need to start the activity because it is not currently
                // running, and the caller has asked to clear the current task to have this
                // activity at the top.
                mAddingToTask = true;
                // Now pretend like this activity is being started by the top of its task, so it
                // is put in the right place.
                mSourceRecord = intentActivity;
                final TaskRecord task = mSourceRecord.task;
                if (task != null && task.stack == null) {
                    // Target stack got cleared when we all activities were removed above.
                    // Go ahead and reset it.
                    mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                            null /* bounds */, mLaunchFlags, mOptions);
                    mTargetStack.addTask(task,
                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
                }
            }
        } else if (mStartActivity.realActivity.equals(intentActivity.task.realActivity)) {
            // In this case the top activity on the task is the same as the one being launched,
            // so we take that as a request to bring the task to the foreground. If the top
            // activity in the task is the root activity, deliver this new intent to it if it
            // desires.
            if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
                    && intentActivity.realActivity.equals(mStartActivity.realActivity)) {
                ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
                        intentActivity.task);
                if (intentActivity.frontOfTask) {
                    intentActivity.task.setIntent(mStartActivity);
                }
                intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
                        mStartActivity.launchedFromPackage);
            } else if (!intentActivity.task.isSameIntentFilter(mStartActivity)) {
                // In this case we are launching the root activity of the task, but with a
                // different intent. We should start a new instance on top.
                mAddingToTask = true;
                mSourceRecord = intentActivity;
            }
        } else if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) == 0) {
            // In this case an activity is being launched in to an existing task, without
            // resetting that task. This is typically the situation of launching an activity
            // from a notification or shortcut. We want to place the new activity on top of the
            // current task.
            mAddingToTask = true;
            mSourceRecord = intentActivity;
        } else if (!intentActivity.task.rootWasReset) {
            // In this case we are launching into an existing task that has not yet been started
            // from its front door. The current task has been brought to the front. Ideally,
            // we'd probably like to place this new task at the bottom of its stack, but that's
            // a little hard to do with the current organization of the code so for now we'll
            // just drop it.
            intentActivity.task.setIntent(mStartActivity);
        }
    }

從上述代碼的注釋轮锥,可以很清楚的得到以下結論:

  • 如果 Intent flag 同時設置有 FLAG_ACTIVITY_CLEAR_TASK 與 FLAG_ACTIVITY_NEW_TASK 那么會把當前的棧清空矫钓。目標Activity 啟動的時候,與棧內(nèi)之前已經(jīng)存在的 Activity 不是一個實例舍杜。
  • 如果目標 Activity launchMode 為 singleTask或者 singleInstance ,那么首先清除棧里面的元素新娜,直到棧頂元素為將要啟動的目標 ActivityRecord 實例為止。
  • 若Intent Flag 中包含 FLAG_ACTIVITY_CLEAR_TOP 既绩, 則也執(zhí)行清棧操作概龄,但是會把已經(jīng)存在的 ActivityRecord 首先 finish掉,然后重新啟動目標 ActivityRecord 實例饲握。
??自此可以 FLAG_ACTIVITY_NEW_TASK 和 singleTask 的區(qū)別了私杜,singleTask 會執(zhí)行清除棧的操作,直到棧頂元素為已經(jīng)存在的目標 ActivityRecord 實例救欧,并不會啟動一個新的 Activity 實例衰粹。如果launchMode 為標準啟動模式,并且僅僅是將IntentFlag 為 FLAG_ACTIVITY_NEW_TASK 颜矿,那么僅僅只會復用這個已經(jīng)存在的棧寄猩,并不會執(zhí)行清除棧的操作,而是僅僅新建一個新的ActivityRecord 實例出來骑疆,把它放在棧頂而已田篇。
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末替废,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子泊柬,更是在濱河造成了極大的恐慌椎镣,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兽赁,死亡現(xiàn)場離奇詭異状答,居然都是意外死亡,警方通過查閱死者的電腦和手機刀崖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門惊科,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亮钦,你說我怎么就攤上這事馆截。” “怎么了蜂莉?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵蜡娶,是天一觀的道長。 經(jīng)常有香客問我映穗,道長窖张,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任蚁滋,我火速辦了婚禮宿接,結果婚禮上,老公的妹妹穿的比我還像新娘枢赔。我一直安慰自己澄阳,他們只是感情好拥知,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布踏拜。 她就那樣靜靜地躺著,像睡著了一般低剔。 火紅的嫁衣襯著肌膚如雪速梗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天襟齿,我揣著相機與錄音姻锁,去河邊找鬼。 笑死猜欺,一個胖子當著我的面吹牛位隶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播开皿,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼涧黄,長吁一口氣:“原來是場噩夢啊……” “哼篮昧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起笋妥,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤懊昨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后春宣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體酵颁,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年月帝,在試婚紗的時候發(fā)現(xiàn)自己被綠了躏惋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡嚷辅,死狀恐怖其掂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潦蝇,我是刑警寧澤款熬,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站攘乒,受9級特大地震影響贤牛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜则酝,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一殉簸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沽讹,春花似錦般卑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挚瘟,卻和暖如春叹谁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背乘盖。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工焰檩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人订框。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓析苫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子衩侥,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

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