深入分析Android中Activity的onStop和onDestroy()回調(diào)延時及延時10s的問題

一细办、起因

  1. 很久以前接手的一個比較老的項目中瞪浸,是使用Activity的名字作為tag來標識網(wǎng)絡請求的漓糙。在Activity的onDestroy回調(diào)中根據(jù)這個標識取消所有的網(wǎng)絡請求潦匈。但是在部分頁面闹丐,出現(xiàn)了比較奇怪的問題:從Activity A打開Activity B横殴,然后finish掉Activity B回到Activity A,這時候再次打開Activity B卿拴,Activity B中的網(wǎng)絡請求會出現(xiàn)概率性無回調(diào)導致頁面加載失敗滥玷,且毫無規(guī)律可循。后經(jīng)過仔細排查巍棱,原來是第一次打開的Activity B實例onDestroy回調(diào)延時造成的惑畴。也就是說,由于第一次打開的Activity B實例的onDestroy回調(diào)延時航徙,恰好在第二次打開的Activity B實例啟動后正在請求網(wǎng)絡的時候被執(zhí)行如贷,取消了全部以Activity B名字作為tag的網(wǎng)絡請求,當然會導致第二次打開的Activity B實例中網(wǎng)絡請求無回調(diào)了到踏。(此處暫不討論這種取消網(wǎng)絡請求的不合理性)
  2. 在某個項目中杠袱,產(chǎn)品經(jīng)理要求實現(xiàn)應用內(nèi)浮窗,根據(jù)各種需求細節(jié)窝稿,最終敲定的技術方案為:使用WindowManager來加載一個自定義浮窗楣富,并根據(jù)Activity的生命周期回調(diào)來確定當前App是在前臺還是后臺。最先想到的自然是維護一個計數(shù)器伴榔,在Activity的onStart處將計數(shù)器+1纹蝴,然后再onStop處將計算器-1庄萎。然而實際情況中,經(jīng)常發(fā)現(xiàn)onStop延時調(diào)用塘安,且延時時間不確定糠涛,導致浮窗關閉與App切換前后臺時間不同步。最終只能改用onResume和onPause兼犯,外加一大堆冗余判斷來保證浮窗能正常顯示關閉忍捡。
  3. 很多時候由于項目開發(fā)周期緊,碰到問題沒時間深究切黔,為了趕工砸脊,只是采取替代方案快速解決/繞開了存在的問題。這段時間需求少了一點纬霞,開始對項目中的一些重點頁面做性能分析和優(yōu)化凌埂。當對一個復雜的列表頁和詳情頁進行檢查的時候,再一次發(fā)現(xiàn)了onStop和onDestroy回調(diào)延時的問題险领。簡單分析無果侨舆,加之又回想起那些被未知原因的bug支配的恐懼,于是下定決心绢陌,要趁著現(xiàn)在有空來深入挖掘下回調(diào)延時的背后究竟隱藏著哪些不為人知的秘密挨下。

二、原因/結(jié)論

Activity調(diào)用流程:打開Activity A -> 打開Activity B -> 關閉Activity B脐湾,回到Activity A

  1. 回調(diào)延時:
    由于要關閉的Activity B或者將要打開的Activity A往主線程的MessageQueue中連續(xù)不斷的post了大量的msg臭笆,導致主線程一直在不斷的進行消息循環(huán)處理這些消息而沒有得到停歇。因此秤掌,ActivityThread中Idler.queueIdle方法沒有被調(diào)用的機會愁铺,App側(cè)也就不能向ActivityManagerService(AMS)發(fā)起IPC告知自己有空閑時間來處理AMS側(cè)的任務,也就是AMS向App側(cè)發(fā)起IPC來進行Activity B的銷毀的正常流程被阻斷了闻鉴。所以茵乱,finish Activity B后,onDestroy不會被及時回調(diào)孟岛。具體延時多久瓶竭,要看主線程中堆積的msg什么時候被處理完,也就是說要看主線程什么時候閑下來渠羞。
  2. 延時10s:
    除正常流程外斤贰,Android系統(tǒng)另行安排了一套流程來保證即使正常流程被阻斷以后,Activity B還是能被銷毀次询。這個流程就是用來預防正常流暢中App不會閑下來處理AMS側(cè)的IPC任務而設計的(源碼注釋:Schedule an idle timeout in case the app doesn't do it for us)荧恍。流程具體為:在關閉Activity B返回Activity A的時候,當AMS側(cè)發(fā)起IPC通知App側(cè)的Activity A進行resume的時候屯吊,同時也會向AMS自己的主線程發(fā)送一個msg送巡,該msg延時10s后執(zhí)行摹菠。該msg的處理方為ActivityStackSupervisor#ActivityStackSupervisorHandler,msg的具體內(nèi)容與正常流程App段空閑時需要執(zhí)行的任務內(nèi)容一致授艰,當然也包括銷毀Activity B辨嗽。這樣世落,Activity B的onDestroy方法也就在延時10s后被調(diào)用了淮腾。

三、問題復現(xiàn)

由于實際項目中出現(xiàn)onStop和onDestroy回調(diào)延時的頁面代碼較為復雜屉佳,難以抽取展示谷朝。因而這里只給出一個簡單的示例來演示、復現(xiàn)問題武花。我們創(chuàng)建兩個Activity圆凰,分別為FirstActivity和SecondActivity,并在SecondActivity創(chuàng)建的時候開始往主線程不斷的post空msg体箕,如下:

// in SecondActivity.java
public class SecondActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        ...
        postMsg();
    }

    private void postMsg() {
       view.post(new Runnable() {
            @Override
            public void run() {
                try {
                    // 在主線程中休眠一小段時間
                    // 用來模擬主線程中諸如復雜的繪制专钉、復雜數(shù)據(jù)處理、幀動畫等等操作
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
       view.postDelayed(new Runnable() {
            @Override
            public void run() {
                postMsg();
            }
        }, 10);
    }
}

代碼非常簡單累铅。我們知道跃须,view.post()方法底層就是往主線程的消息隊列MessageQueue發(fā)送消息(這里這么寫只是為了少寫兩行代碼),因為整段代碼邏輯就是娃兽,在SecondActivity中不斷向主線程post空msg菇民,來模擬復雜頁面中不停的重繪、數(shù)據(jù)流處理投储、幀動畫處理等第练。
測試流程:我們首先打開FirstActivity,然后在FirstActivity中通過StartActivity方法來打開SecondActivity玛荞,接著我們按下回退鍵娇掏,關閉SecondActivity回到FirstActivity。如果我們打印出FirstActivity和SecondActivity的生命周期回調(diào)勋眯,就不難發(fā)現(xiàn)婴梧,按下回退鍵后,在FirstActivity的onResume方法被調(diào)用后凡恍,SecondActivity的onStop和onDestroy方法并沒有緊接著被回調(diào)志秃,而是恰好在10s后才被回調(diào)。

四嚼酝、源碼探究

既然出現(xiàn)了問題浮还,當然要想辦法弄清楚才行。于是我們熟練的打開百度闽巩,輸入onDestroy回調(diào)延時钧舌,隨便找兩篇博客看看也就知道答案了担汤。
本文分析到此為止。
開個玩笑洼冻,要是常規(guī)操作能解決問題崭歧,我當然也就不用多此一舉來寫這么一篇問題分析的博文了。這里隨便貼幾篇百度搜到的博文:

看完很絕望有沒有啊率碾。雖然很多博文都寫著原因分析,但完全沒有普適性屋彪。給出的解決方案也是要么繞開這個坑所宰,要么明著告訴你不要在onDestroy回調(diào)中去做balabala...。既然百度不行畜挥,那咱Google一下吧...Google好像也不太好使(或者說咱用的不好?)...還是不行仔粥,去stackoverflow看看,鑒于咱這英文閱讀速度和搜出來的質(zhì)量蟹但,在換了幾個關鍵詞之后也就放棄了躯泰。得,還是自己動手华糖,豐衣足食吧麦向。
首先從內(nèi)存泄漏的角度考察了一番,什么LeakCanary缅阳、Android Profile和大殺器MAT磕蛇,通通祭出來。結(jié)果鬧了半天十办,仍然沒有什么重大發(fā)現(xiàn)秀撇。看來是個棘手得問題向族,真叫人頭禿啊呵燕,差點泄氣要放棄了〖啵可是留下這個問題再扭,又讓人恨得牙癢癢夜矗,甚至就此留下陰影,成為以后職業(yè)生涯修煉時走火入魔的導火索紊撕。哎,既然捷徑都走不通区赵,那看來只能笨辦法,源碼分析走起漱受,我倒要看看系統(tǒng)這個鬼究竟搞了什么騷操作骡送。
我們就以Activity的finish方法作為入口,一步一步各谚,深入分析其流程紧憾。
本次分析到千,源碼基于Android 9.0昌渤。其中會有基于Binder的進程間通信和基于Handler的進程內(nèi)線程間通信相關知識,因為不是分析重點憔四,一筆帶過膀息。另外onStop方法延遲調(diào)用與onDestroy方法延遲調(diào)用的原因有關聯(lián),分析流程也基本一致了赵,所以這里以分析onDestroy方法延遲為主線潜支。

// in Activity.java
public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback, WindowControllerCallback,
        AutofillManager.AutofillClient {

    public void finish() {
        finish(DONT_FINISH_TASK_WITH_ACTIVITY);
    }

    private void finish(int finishTask) {
        if (mParent == null) {
            ...
            try {
                if (resultData != null) {
                    resultData.prepareToLeaveProcess(this);
                }
                if (ActivityManager.getService()
                        .finishActivity(mToken, resultCode, resultData, finishTask)) {
                    mFinished = true;
                }
            } catch (RemoteException e) {
                // Empty
            }
            ...
        }
    }
}

這個流程還是比較簡單的,finish方法內(nèi)部調(diào)用了一個帶參數(shù)的私有finish方法柿汛,參數(shù)DONT_FINISH_TASK_WITH_ACTIVITY從字面意思也可以看出來冗酿,就是只關閉Activity而不關閉Activity所在的任務棧。帶參數(shù)的finish方法中調(diào)用了ActivityManager.getService().finishActivity(mToken, resultCode, resultData, finishTask)络断,通過IPC向AMS發(fā)起調(diào)用關閉當前Activity裁替。

// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
            int finishTask) {
            ...
            try {
                boolean res;
                final boolean finishWithRootActivity =
                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
                        || (finishWithRootActivity && r == rootR)) {
                    res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false,
                            finishWithRootActivity, "finish-activity");
                    if (!res) {
                        Slog.i(TAG, "Removing task failed to finish activity");
                    }
                } else {
                    // 由于finishtask標志為DONT_FINISH_TASK_WITH_ACTIVITY
                    // 因此這里進入else分支
                    res = tr.getStack().requestFinishActivityLocked(token, resultCode,
                            resultData, "app-request", true);
                    if (!res) {
                        Slog.i(TAG, "Failed to finish by app-request");
                    }
                }
                return res;
            } finally {
                Binder.restoreCallingIdentity(origId);
            }
            ...
    }
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {

    final boolean requestFinishActivityLocked(IBinder token, int resultCode,
            Intent resultData, String reason, boolean oomAdj) {
        ...
        finishActivityLocked(r, resultCode, resultData, reason, oomAdj);
        ...
    }

    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
            String reason, boolean oomAdj) {
        //PAUSE_IMMEDIATELY定義在ActivityStackSupervisor中
        //static final boolean PAUSE_IMMEDIATELY = true;
        //即不要立即執(zhí)行暫停該Activity后的流程,而是延時500ms后再進行暫停Activity后的相關工作
        //延時500ms其實也是一種保險機制貌笨,確保后續(xù)流程一定會被執(zhí)行弱判。正常的話App側(cè)在onPause后會發(fā)起IPC來告知AMS執(zhí)行后續(xù)流程
        return finishActivityLocked(r, resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
    }

    final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,
            String reason, boolean oomAdj, boolean pauseImmediately) {
        if (r.finishing) {
            Slog.w(TAG, "Duplicate finish request for " + r);
            return false;
        }

        mWindowManager.deferSurfaceLayout();
        try {
            //設置r.finishing=true; 標記當前Activity為finishing
            r.makeFinishingLocked();
            final TaskRecord task = r.getTask();
            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
                    r.userId, System.identityHashCode(r),
                    task.taskId, r.shortComponentName, reason);
            final ArrayList<ActivityRecord> activities = task.mActivities;
            final int index = activities.indexOf(r);
            if (index < (activities.size() - 1)) {
                task.setFrontOfTask();
                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
                    // If the caller asked that this activity (and all above it)
                    // be cleared when the task is reset, don't lose that information,
                    // but propagate it up to the next activity.
                    ActivityRecord next = activities.get(index+1);
                    next.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
                }
            }
            //停止向當前Activity分發(fā)按鍵消息事件
            r.pauseKeyDispatchingLocked();
            //調(diào)整當前的活動的任務棧
            adjustFocusedActivityStack(r, "finishActivity");
            //記錄當前Activity要發(fā)送出去的結(jié)果數(shù)據(jù)
            finishActivityResultsLocked(r, resultCode, resultData);

            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
            final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
            if (mResumedActivity == r) {//此處,當前Activity即為活動Activity锥惋,進入if分支
                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                        "Prepare close transition: finishing " + r);
                if (endTask) {
                    mService.mTaskChangeNotificationController.notifyTaskRemovalStarted(
                            task.taskId);
                }
                mWindowManager.prepareAppTransition(transit, false);

                // Tell window manager to prepare for this one to be removed.
                將當前Activity設置為不可見
                r.setVisibility(false);

                if (mPausingActivity == null) {//還未暫停當前Activity,因此mPausingActivity為null
                    //開始暫停當前Activity
                    startPausingLocked(false, false, null, pauseImmediately);
                }

                if (endTask) {
                    mService.getLockTaskController().clearLockedTask(task);
                }
            } else if (!r.isState(PAUSING)) {
                ...
            } else {
                ...
            }

            return false;
        } finally {
            ...
        }
    }

    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming, boolean pauseImmediately) {
        if (mPausingActivity != null) {
            if (!shouldSleepActivities()) { //很明顯,正常情況這里為false
                completePauseLocked(false, resuming);
            }
        }

        ActivityRecord prev = mResumedActivity;
        ...
        mPausingActivity = prev;
        mLastPausedActivity = prev;
        mLastNoHistoryActivity = (prev.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (prev.info.flags & ActivityInfo.FLAG_NO_HISTORY) != 0 ? prev : null;
        //設置當前Activity的狀態(tài)為pausing
        prev.setState(PAUSING, "startPausingLocked");
        prev.getTask().touchActiveTime();
        clearLaunchTime(prev);
        mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode());
        mService.updateCpuStats();

        if (prev.app != null && prev.app.thread != null) {
            try {
                EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName, "userLeaving=" + userLeaving);
                mService.updateUsageStats(prev, false);
                //發(fā)起IPC通信劫流,通知App側(cè)暫定當前Activity大审,最終會回調(diào)Activity的onPause方法
                mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                        PauseActivityItem.obtain(prev.finishing, userLeaving,
                                prev.configChangeFlags, pauseImmediately));
            } catch (Exception e) {
                ...
            }
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }

        if (mPausingActivity != null) { //前面已經(jīng)將當前Activity賦值給mPausingActivity了徒扶,因此這里不為null
            ...
            if (pauseImmediately) {//前面我們傳進來的參數(shù)為!PAUSE_IMMEDIATELY姜骡,因此進入else分支
                // If the caller said they don't want to wait for the pause, then complete
                // the pause now.
                completePauseLocked(false, resuming);
                return false;
            } else {
                //向AMS主線程發(fā)送一個延時500ms的消息,
                schedulePauseTimeout(prev);
                return true;
            }
        } else {
            ...
        }
    }
}

為了使邏輯看起來更清晰康栈,這里刪減了一部分源碼啥么,并根據(jù)調(diào)用流程悬荣,調(diào)整了方法的排列順序氯迂,同時對部分較為重要的源碼進行了注釋嚼蚀。
整個流程方法調(diào)用鏈為:在AMS中發(fā)起調(diào)用finishActivity方法,進入ActivityStack中哮独,接著依次調(diào)用了requestFinishActivityLocked -> finishActivityLocked -> finishActivityLocked -> startPausingLocked這幾個方法舟扎。在第一個調(diào)用方法finishActivityLocked中睹限,我們詳細注釋了pauseImmediately參數(shù)的意義羡疗。最后叨恨,在startPausingLocked方法中秉颗,我們我們看到了這么一句代碼:

mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken,
                        PauseActivityItem.obtain(prev.finishing, userLeaving,
                                prev.configChangeFlags, pauseImmediately));

其實就是AMS通過Binder與App側(cè)進行IPC蚕甥,App側(cè)ActivityThread中的ApplicationThread即為本次IPC通信的對端菇怀。然后App側(cè)再通過Handler進行進程內(nèi)線程間通信敏释,通知ActivityThread中的H(繼承Handler)來處理消息义屏。H中TransactionExecutor作為真正的msg執(zhí)行者闽铐,統(tǒng)一處理消息中所攜帶的任務兄墅。處理方式即執(zhí)行PauseActivityItem中的execute和postExecute方法:

// in TransactionExecutor.java
public class TransactionExecutor {
    private void executeLifecycleState(ClientTransaction transaction) {
        //這里的lifecycleItem即為前面AMS傳遞過來的PauseActivityItem
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
        ...
        // Execute the final transition with proper parameters.
        //調(diào)用PauseActivityItem中的execute和postExecute方法
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }
}

我們進入PauseActivityItem看看AMS發(fā)起IPC究竟要干什么:

// in PauseActivityItem.java
public class PauseActivityItem extends ActivityLifecycleItem {
    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        //調(diào)用ActivityThread的handlePauseActivity方法沐悦,最終會回調(diào)當前Activity的onPause
        client.handlePauseActivity(token, mFinished, mUserLeaving, mConfigChanges, pendingActions,
                "PAUSE_ACTIVITY_ITEM");
    }

    @Override
    public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        if (mDontReport) {
            return;
        }
        try {
            // TODO(lifecycler): Use interface callback instead of AMS.
            // 向AMS發(fā)起IPC藏否,告知AMS當前Activity已經(jīng)暫停,可以執(zhí)行后續(xù)流程了
            // IPC對端是ActivityManagerService淆储,最終AMS中的activityPaused方法會被調(diào)用
            ActivityManager.getService().activityPaused(token);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

PauseActivityItem一共干了兩件事本砰,首先是回調(diào)當前Activity的onPause青团,然后發(fā)起IPC督笆,告訴AMS可以執(zhí)行后續(xù)流程了娃肿。我們進入ActivityManagerService來看看activityPaused方法:

// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

    @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);
    }
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {

    final void activityPausedLocked(IBinder token, boolean timeout) {
        final ActivityRecord r = isInStackLocked(token);
        if (r != null) {
            //前面我們發(fā)送了一個延時500ms的消息來確保后續(xù)流程一定能夠得以執(zhí)行
            //現(xiàn)在正常流程沒問題,因此移除延時消息
            mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
            if (mPausingActivity == r) {
                mService.mWindowManager.deferSurfaceLayout();
                try {
                    //開始執(zhí)行當前Activity暫停后的流程
                    //參數(shù)resumeNext為true晒杈,表示需要喚起下一個Activity
                    completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
                } finally {
                    mService.mWindowManager.continueSurfaceLayout();
                }
                return;
            } else {
                ...
            }
        }
        //確保全部Activity的可見性都處于正確的狀態(tài)
        mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
    }

    private void completePauseLocked(boolean resumeNext, ActivityRecord resuming) {
        ActivityRecord prev = mPausingActivity;

        if (prev != null) {
            prev.setWillCloseOrEnterPip(false);
            final boolean wasStopping = prev.isState(STOPPING);
            prev.setState(PAUSED, "completePausedLocked");
            if (prev.finishing) { //早在finishActivityLocked方法中就通過r.makeFinishingLocked()將finishing置為true了
                //finish當前Activity
                prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE, false,
                        "completedPausedLocked");
            } else if (prev.app != null) {
                ...
            } else {
                ...
            }
            if (prev != null) {
                prev.stopFreezingScreenLocked(true /*force*/);
            }
            mPausingActivity = null;
        }

        if (resumeNext) { //傳遞進來的resumeNext為true
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if (!topStack.shouldSleepOrShutDownActivities()) { //看字面意思也可猜到,正常情況下進入if分支
                mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
            } else {
                ...
            }
        }
        ...
        //再次確保全部Activity的可見性都處于正確的狀態(tài)
        mStackSupervisor.ensureActivitiesVisibleLocked(resuming, 0, !PRESERVE_WINDOWS);
    }

    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
            String reason) {
        //獲取任務棧中棧頂Activity作為接下來即將要顯示的Activity
        final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(
                true /* considerKeyguardState */);
        ...
        final ActivityState prevState = r.getState();
        r.setState(FINISHING, "finishCurrentActivityLocked");
        //很明顯粪般,當前任務棧即為前臺活動任務棧亩歹,因此finishingActivityInNonFocusedStack為false
        final boolean finishingActivityInNonFocusedStack
                = r.getStack() != mStackSupervisor.getFocusedStack()
                && prevState == PAUSED && mode == FINISH_AFTER_VISIBLE;
        if (mode == FINISH_IMMEDIATELY // mode為FINISH_AFTER_VISIBLE false
                || (prevState == PAUSED
                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode())) // inPinnedWindowingMode() 為false
                || finishingActivityInNonFocusedStack //false
                || prevState == STOPPING //prevState應該為PAUSED
                || prevState == STOPPED
                || prevState == ActivityState.INITIALIZING) {
            //看起來是要銷毀當前Activity了,好開心...然而躲惰,判斷結(jié)果為false础拨,壓根不會進到if分支里面來
            r.makeFinishingLocked();
            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);
            ...
        }
        //雖然因為條件不符合诡宗,這里還沒辦法直接銷毀當前Activity塔沃。但是蛀柴,我們先將當前Activity放到一個列表中鸽疾,該列表存儲著全部即將要銷毀的Activity
        mStackSupervisor.mFinishingActivities.add(r);
        r.resumeKeyDispatchingLocked();
        mStackSupervisor.resumeFocusedStackTopActivityLocked();
        return r;
    }
}

經(jīng)過IPC冒窍,AMS側(cè)收到消息综液,調(diào)用ActivityStack的activityPausedLocked方法開始繼續(xù)后續(xù)流程谬莹。我們看到届良,這里ActivityStack中方法調(diào)用鏈為:activityPausedLocked -> completePauseLocked -> finishCurrentActivityLocked。本來追蹤到finishCurrentActivityLocked方法送悔,看到destroyActivityLocked這句代碼時挺開心的欠啤,以為這就是終點了。然而共郭,經(jīng)過仔細分析,條件根本不符合岸蜗,整個if分支根本得不到執(zhí)行璃岳。因此铃慷,finishCurrentActivityLocked方法中僅僅是將當前Activity放到一個存儲著全部要銷毀的Activity列表里犁柜。再次回completePauseLocked方法中,由于需要喚起下一個Activity淤齐,因此稚疹,調(diào)用了

//注意參數(shù)内狗。第一個參數(shù)為當前前臺活動的任務棧柳沙,第二個參數(shù)為當前正在關閉的Activity
mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);

這行代碼赂鲤,我們進入StackSupervisor数初,看看resumeFocusedStackTopActivityLocked方法都干了啥:

// in StackSupervisor.java
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
        RecentTasks.Callbacks {

    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        ...
        if (targetStack != null && isFocusedStack(targetStack)) {
            //根據(jù)傳進來的參數(shù),很容易知道進入if分支
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        ...
    }
}
// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {

    boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        ...
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            //喚起/顯示棧頂?shù)腁ctivity
            result = resumeTopActivityInnerLocked(prev, options);

            // When resuming the top activity, it may be necessary to pause the top activity (for
            // example, returning to the lock screen. We suppress the normal pause logic in
            // {@link #resumeTopActivityUncheckedLocked}, since the top activity is resumed at the
            // end. We call the {@link ActivityStackSupervisor#checkReadyForSleepLocked} again here
            // to ensure any necessary pause logic occurs. In the case where the Activity will be
            // shown regardless of the lock screen, the call to
            // {@link ActivityStackSupervisor#checkReadyForSleepLocked} is skipped.
            final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
            if (next == null || !next.canTurnScreenOn()) {
                checkReadyForSleep();
            }
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        ...
    }

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ...
        //方法很長仑鸥,內(nèi)容也很多薄料,限于篇幅摄职,這里就不仔細分析了谷市。
        //主要是調(diào)用了:
        if (next.app != null && next.app.thread != null) {
            ...
            synchronized(mWindowManager.getWindowManagerLock()) {
                try {
                    ...
                    //發(fā)起IPC迫悠,通知App側(cè)要打開的Activity创泄,最終會回調(diào)下一個要打開的Activity的onResume
                    transaction.setLifecycleStateRequest(
                            ResumeActivityItem.obtain(next.app.repProcState,
                                    mService.isNextTransitionForward()));
                    mService.getLifecycleManager().scheduleTransaction(transaction);
                } catch (Exception e) {
                    ...
                }
            }

            try {
                // 除了通知App側(cè)下一個要打開的Activity,AMS側(cè)也要完成其他狀態(tài)設置相關的流程
                next.completeResumeLocked();
            } catch (Exception e) {
                // If any exception gets thrown, toss away this
                // activity and try the next one.
                Slog.w(TAG, "Exception thrown during resume of " + next, e);
                requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
                        "resume-exception", true);
                if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
                return true;
            }
            ...
        }
        ...
    }
}

至此搁拙,我們需要兵分兩路箕速。一路是追蹤AMS向App發(fā)起的IPC盐茎,喚起下一個要顯示的Activity庭呜;一路是追蹤喚起下一個Activity后AMS本身要進行的狀態(tài)設置相關工作。我們先跟蹤IPC流程阴汇,ResumeActivityItem與前面我們分析的PauseActivityItem一樣搀庶,都是先執(zhí)行execute方法哥倔,再執(zhí)行postExecute方法:

// in ResumeActivityItem.java
public class ResumeActivityItem extends ActivityLifecycleItem {

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        //調(diào)用ActivityThread的handleResumeActivity方法东抹,最終會回調(diào)下一個要顯示的Activity的onResume
        client.handleResumeActivity(token, true /* finalStateRequest */, mIsForward,
                "RESUME_ACTIVITY");
    }

    @Override
    public void postExecute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        try {
            // TODO(lifecycler): Use interface callback instead of AMS.
            // 向AMS發(fā)起IPC,告知AMS下一個要顯示的Activity已經(jīng)resume蒂破,可以執(zhí)行后續(xù)流程了
            // IPC對端是ActivityManagerService惧互,最終AMS的activityResumed方法會被調(diào)用
            ActivityManager.getService().activityResumed(token);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
    }
}

ResumeActivityItem也就干了兩件事喊儡,首先是回調(diào)下一個要顯示的Activity的onResume管宵,然后發(fā)起IPC箩朴,告訴AMS可以執(zhí)行后續(xù)流程了炸庞。我們再次進入ActivityManagerService來看看activityResumed方法:

// in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

    @Override
    public final void activityResumed(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityRecord.activityResumedLocked(token);
            mWindowManager.notifyAppResumedFinished(token);
        }
        Binder.restoreCallingIdentity(origId);
    }
}

如果從AMS再繼續(xù)跟蹤下去,線索就斷了滥壕。千辛萬苦分析到此處绎橘,結(jié)果還是沒能挖出真相称鳞?心里一萬頭草泥馬呼嘯而過。沒辦法狂票,生活還是要繼續(xù)闺属,讓我們收拾好心情屋剑,回溯到上一個方法唉匾,看看ActivityThread的handleResumeActivity方法都干了些啥:

// in ActivityThread.java
public final class ActivityThread extends ClientTransactionHandler {

    @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...
        往主線程的消息隊列添加了一個空閑處理器巍膘,當主線程空閑時就會回調(diào)這個處理器來執(zhí)行一些優(yōu)先級較低的任務
        Looper.myQueue().addIdleHandler(new Idler());
    }

    private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            boolean stopProfiling = false;
            if (mBoundApplication != null && mProfiler.profileFd != null
                    && mProfiler.autoStopProfiler) {
                stopProfiling = true;
            }
            if (a != null) {
                mNewActivities = null;
                IActivityManager am = ActivityManager.getService();
                ActivityClientRecord prev;
                do {
                    if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    if (a.activity != null && !a.activity.mFinished) {
                        try {
                            //發(fā)起IPC肪康,告訴AMS側(cè)磷支,我(App側(cè))空閑了,你有什么事情需要我做的抵皱,趕緊扔過來吧
                            //最終會調(diào)用ActivityManagerService的activityIdle方法
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                            a.createdConfig = null;
                        } catch (RemoteException ex) {
                            throw ex.rethrowFromSystemServer();
                        }
                    }
                    prev = a;
                    a = a.nextIdle;
                    prev.nextIdle = null;
                } while (a != null);
            }
            ...
        }
    }
}

關鍵的代碼我已經(jīng)注釋起來了移盆,總而言之就是App在下一個要顯示的Activity調(diào)用完onResume后往自己的主線程消息隊列注冊一個空閑處理器味滞,以便App主線程的消息隊列中全部msg處理完了以后能去處理一些低優(yōu)先級的任務爽醋。這個空閑處理器的調(diào)用是一次性的蚂四,也就是說調(diào)用一次之后就不會再被調(diào)用了遂赠。而這個所謂的低優(yōu)先級任務筷弦,其實就是App側(cè)向AMS側(cè)發(fā)起IPC烂琴,告訴AMS,我(App)閑下來了号醉,你有什么需要我干的任務沒有畔派。進入ActivityManagerService父虑,看看activityIdle方法:

//in ActivityManagerService
public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    @Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        synchronized (this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                //App側(cè)空閑了士嚎,要干點什么好呢莱衩?
                ActivityRecord r =
                        mStackSupervisor.activityIdleInternalLocked(token, false /* fromTimeout */,
                                false /* processPausingActivities */, config);
                if (stopProfiling) {
                    if ((mProfileProc == r.app) && mProfilerInfo != null) {
                        clearProfilerLocked();
                    }
                }
            }
        }
        Binder.restoreCallingIdentity(origId);
    }
}
//in StackSupervisor
public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
        RecentTasks.Callbacks {
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
        ...
        // Finish any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        //還記得前面我們分析的Activity銷毀流程吧,
        //在ActivityStack的finishCurrentActivityLocked方法中括细,眼看就要成功,卻因條件不符功虧一簣的地方嗎呆盖?
        //雖然條件不符应又,未能調(diào)用destroyActivityLocked去銷毀Activity株扛,但當時卻將需要待銷毀的Activity加入到一個列表中了。
        //此處的finishes就是那個待銷毀的Activity列表
        for (int i = 0; i < NF; i++) {
            r = finishes.get(i);
            final ActivityStack stack = r.getStack();
            if (stack != null) {
                activityRemoved |= stack.destroyActivityLocked(r, true, "finish-idle");
            }
        }
        ...
    }
}

分析這么久奖磁,終于看到希望的曙光了咖为。再接再厲,進入ActivityStack吞彤,速度瞟一眼destroyActivityLocked方法吧:

// in ActivityStack.java
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {
    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
        ...
        final boolean hadApp = r.app != null;
        if (hadApp) {
            ...
            boolean skipDestroy = false;
            try {
                //發(fā)起IPC,通知App側(cè)銷毀ActivityB袂丁1⑧隆了罪!
                mService.getLifecycleManager().scheduleTransaction(r.app.thread, r.appToken,
                        DestroyActivityItem.obtain(r.finishing, r.configChangeFlags));
            } catch (Exception e) {
                ...
            }

            r.nowVisible = false;

            // If the activity is finishing, we need to wait on removing it
            // from the list to give it a chance to do its cleanup. During
            // that time it may make calls back with its token so we need to
            // be able to find it on the list and so we don't want to remove
            // it from the list yet. Otherwise, we can just immediately put
            // it in the destroyed state since we are not removing it from the
            // list.
            if (r.finishing && !skipDestroy) {
                r.setState(DESTROYING,
                        "destroyActivityLocked. finishing and not skipping destroy");
                //向AMS主線程發(fā)起一個延時10s的消息田藐,保證即使正常流程失敗踊餐,也能進行Activity銷毀的后續(xù)流程
                //這里也是10s,頗具誤導性窜管。
                //在這里繞了好久時間幕帆,最終發(fā)現(xiàn)失乾,這個分支流程與我們要分析的問題關系不大
                Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG, r);
                mHandler.sendMessageDelayed(msg, DESTROY_TIMEOUT);
            } else {
                r.setState(DESTROYED,
                        "destroyActivityLocked. not finishing or skipping destroy");
                r.app = null;
            }
        } else {
            ...
        }
    }
}

老規(guī)矩,讓我們看看AMS發(fā)起IPC纽竣,通知App側(cè)都干了啥。進入DestroyActivityItem茧泪,看看execute方法和postExecute方法:

public class DestroyActivityItem extends ActivityLifecycleItem {
    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        //調(diào)用ActivityThread的handleDestroyActivity蜓氨,最終會調(diào)用Activity的onDestroy方法
        client.handleDestroyActivity(token, mFinished, mConfigChanges,
                false /* getNonConfigInstance */, "DestroyActivityItem");
    }
}

So easy~。呃调炬,postExecute方法哪去了语盈?簡單,我們一路跟蹤DestroyActivityItem的父類刀荒,會發(fā)現(xiàn)postExecute默認實現(xiàn)為一個空方法。
那么,整個分析算是完成了吧。
終于完了啊徽缚,讓我放松下喘口氣先~~
等等D峭瘛:墼ⅰ!onStop方法怎么沒被調(diào)用童谒??為什么回調(diào)會延時??盛嘿?為什么會延時10s??糕伐?
蒼天啊赞庶,搞了這么久,原來問題一個都還沒解決呢??哈哈,其實不是,現(xiàn)在我們離真相只有一步之遙了,一個一個來。

  1. 首先說下onStop方法什么時候被調(diào)用的。
    DestroyActivityItem的execute和postExecute方法誰調(diào)用的啊欠动?前面的分析還沒忘吧,是在TransactionExecutor的executeLifecycleState方法中調(diào)用的:
    // in TransactionExecutor.java
    private void executeLifecycleState(ClientTransaction transaction) {
        // Cycle to the state right before the final requested state.
        //根據(jù)Activity當前的狀態(tài)和最終要到達的狀態(tài)靴迫,計算中間需要經(jīng)過哪些步驟
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */);
    
        // Execute the final transition with proper parameters.
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }
    
    看到?jīng)],關鍵代碼是cycleToPath()参淫,只要稍微一跟蹤就可以知道在Pause和Destroy之間會執(zhí)行Stop了跌前,這個流程簡單沒有其他分支掏缎,因此就不詳寫了忌怎。
  2. onStop和onDestroy回調(diào)為什么會延時
    前面我們已經(jīng)分析完了調(diào)用finish后,到回調(diào)onStop和onDestroy之間的整個正常流程璃弄。這中間經(jīng)歷了多次App側(cè)和AMS側(cè)的進程間通信酌壕,以及兩端各自進程內(nèi)通信。正常流程中較為關鍵的一點是,新的要顯示的Activity在resume后遮怜,App側(cè)的主線程空閑下來才會通知AMS執(zhí)行后續(xù)流程初橘,將關閉的Activity銷毀扔亥。那要是新的Activity在resume后,主線程一直在循環(huán)處理MessageQueue中堆積的msg呢薛训?很顯然辙喂,要是這樣的話,通知AMS執(zhí)行后續(xù)流程自然被延遲了女气,因此,Activity的onStop和onDestroy生命周期回調(diào)自然也就被延遲了。
  3. 延時10s是為什么
    作為一個健壯的操作系統(tǒng)焦读,當然要有一定的容錯機制兆衅,不能說因為App側(cè)主線程一直忙专挪,AMS側(cè)就不去銷毀/回收已經(jīng)死亡的Activity家卖。要不然新手開發(fā)者開發(fā)的App會因為內(nèi)存泄漏等問題分分鐘玩死系統(tǒng)金刁。那么這個容錯機制是怎么設計的呢醇锚?
    前面我們說兵分兩路,一路是追蹤AMS向App發(fā)起的IPC但两,喚起下一個要顯示的Activity训裆,一路是追蹤喚起下一個Activity后AMS本身要進行的狀態(tài)設置相關工作。分析完了AMS側(cè)向App側(cè)發(fā)起IPC后的正常流程后蜀铲,我們繼續(xù)分析AMS本身還做了哪些工作:
    //in ActivityStack.java
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        next.completeResumeLocked();
    }
    
    // in ActivityRecord.java
    final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
        void completeResumeLocked() {
            ...
            // Schedule an idle timeout in case the app doesn't do it for us.
            // 設置一個超時的空閑時間边琉,以便在App沒有通知我們其有空的情形下也能執(zhí)行相關流程
            mStackSupervisor.scheduleIdleTimeoutLocked(this);
            ...
        }
    }
    
    // in StackSupervisor.java
    public class ActivityStackSupervisor extends ConfigurationContainer implements DisplayListener,
            RecentTasks.Callbacks {
        void scheduleIdleTimeoutLocked(ActivityRecord next) {
            //真相就在這里,發(fā)送一個延時10s的消息记劝,確保正常流程行不通的情況下也能銷毀Activity
            Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
            mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
        }
    
        private final class ActivityStackSupervisorHandler extends Handler {
            void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
                synchronized (mService) {
                    //在前面我們分析正常流程的時候变姨,已經(jīng)將該方法的執(zhí)行流程分析完了,不再贅述
                    activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
                            processPausingActivities, null);
                }
            }
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case IDLE_TIMEOUT_MSG: {
                        //延時10s后厌丑,就當Activity側(cè)已經(jīng)空閑下來了定欧,執(zhí)行后續(xù)流程
                        activityIdleInternal((ActivityRecord) msg.obj,
                                true /* processPausingActivities */);
                    } break;
                    ...
                }
            }
    }
    

好了别伏。這下是真的分析完了。
因為源碼是在太長了忧额,因此省略了很多分支流程和與主題無關的代碼。
說是分析回調(diào)延時的問題愧口,其實也即是分析finish的源碼調(diào)用流程睦番。通過分析,我們知道耍属,要想使得Activity的onStop和onDestroy盡快得到回調(diào)托嚣,我們就該在寫代碼的時候,及時關閉厚骗、清理示启、移除不必要的主線程消息,并且盡可能的保證每個消息處理時間不要太長领舰。
經(jīng)過這一波分析夫嗓,該對Framework層App和AMS的交互有了更清晰的理解了吧。
也算是給自己一個交代冲秽,完美舍咖!下班~

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市锉桑,隨后出現(xiàn)的幾起案子排霉,更是在濱河造成了極大的恐慌,老刑警劉巖民轴,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件攻柠,死亡現(xiàn)場離奇詭異,居然都是意外死亡后裸,警方通過查閱死者的電腦和手機瑰钮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轻抱,“玉大人飞涂,你說我怎么就攤上這事∑硭眩” “怎么了较店?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長容燕。 經(jīng)常有香客問我梁呈,道長,這世上最難降的妖魔是什么蘸秘? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任官卡,我火速辦了婚禮蝗茁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寻咒。我一直安慰自己哮翘,他們只是感情好,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布毛秘。 她就那樣靜靜地躺著饭寺,像睡著了一般。 火紅的嫁衣襯著肌膚如雪叫挟。 梳的紋絲不亂的頭發(fā)上艰匙,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機與錄音抹恳,去河邊找鬼员凝。 笑死,一個胖子當著我的面吹牛奋献,可吹牛的內(nèi)容都是我干的健霹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼瓶蚂,長吁一口氣:“原來是場噩夢啊……” “哼骤公!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起扬跋,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤阶捆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后钦听,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洒试,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年朴上,在試婚紗的時候發(fā)現(xiàn)自己被綠了垒棋。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡痪宰,死狀恐怖叼架,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衣撬,我是刑警寧澤乖订,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布具练,位于F島的核電站扛点,受9級特大地震影響哥遮,放射性物質(zhì)發(fā)生泄漏眠饮。R本人自食惡果不足惜奥帘,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一仪召、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咖摹,春花似錦萤晴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趴久。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坎吻,已是汗流浹背刊头。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留穿肄,地道東北人咸产。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親社牲。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

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