踩坑之路:Activity执行finish以后onStop方法10s以后才调用

背景

某天,测试提了一个bug,说当前页面关闭了以后回到了上一个页面,但是对应的音乐并没有立刻停止,而是过了一段时间才停止。于是翻阅了一下代码:

     @Override
    protected void onStop() {
        super.onStop();
        if (mIsLoading) {
            mAudioTool.pausePlayAudio();
        }
    }

mAudioTool.pausePlayAudio方法是停止播放音频的代码,似乎并没有什么问题。而且这段代码已经运行了好久,之前测试也是通过的,为何这个版本会出现这个问题?


所以首先我debug一下当前页面的onStop方法,结果发现页面关闭的时候onStop方法并没有被执行,然后过了差不多10s左右再被执行,感觉又是踩到了某个坑里面去了,仔细回想了最近改了的代码,问题可能出在了上一个页面的下面这段代码:

    /**
     * 开始抖动
     */
    private fun startShake() {
        if(mRotateAnim == null){
            mRotateAnim = RotateAnimation(-2f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)
        }
        if (mRotateAnim!!.hasStarted() && !mRotateAnim!!.hasEnded()) {
            //当前动画已经开始并且没有结束
            return
        }
        //从左向右
        mRotateAnim!!.duration = 50
        mRotateAnim!!.repeatMode = Animation.REVERSE
        mRotateAnim!!.repeatCount = Animation.INFINITE
        val smallAnimationSet = AnimationSet(false)
        smallAnimationSet.addAnimation(mRotateAnim)
        cl_wrong_book.startAnimation(smallAnimationSet)
    }

由于设置的repeatCount是INFINITE,所以动画是一直在执行中的。
不过还是有点纳闷,动画跟Activity生命周期有啥关系?为了印证下到底是不是Animation影响的当前页面生命周期调用异常,于是将这段代码进行了删除操作。结果Activity的onStop生命周期还真的就正常执行了。然后自己又做了一个demo,来查看下onStop的执行时间。
(这段是没有动画的)

2020-12-14 12:40:17.334 24575-24575/com.example.demo I/MainActivity: onCreate
2020-12-14 12:40:17.663 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:17.670 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:20.818 24575-24575/com.example.demo I/MainActivity: onPause
2020-12-14 12:40:20.836 24575-24575/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:40:20.857 24575-24575/com.example.demo I/SecondActivity: onStart
2020-12-14 12:40:20.858 24575-24575/com.example.demo I/SecondActivity: onResume
2020-12-14 12:40:21.406 24575-24575/com.example.demo I/MainActivity: onStop
2020-12-14 12:40:22.930 24575-24575/com.example.demo I/SecondActivity: onPause
2020-12-14 12:40:22.936 24575-24575/com.example.demo I/MainActivity: onStart
2020-12-14 12:40:22.937 24575-24575/com.example.demo I/MainActivity: onResume
2020-12-14 12:40:23.439 24575-24575/com.example.demo I/SecondActivity: onStop
2020-12-14 12:40:23.440 24575-24575/com.example.demo I/SecondActivity: onDestroy

(这段是加上动画的)

2020-12-14 12:38:06.392 24278-24278/com.example.demo I/MainActivity: onCreate
2020-12-14 12:38:06.563 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:06.565 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:23.940 24278-24278/com.example.demo I/MainActivity: onPause
2020-12-14 12:38:23.964 24278-24278/com.example.demo I/SecondActivity: onCreate
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onStart
2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onResume
2020-12-14 12:38:24.544 24278-24278/com.example.demo I/MainActivity: onStop
2020-12-14 12:38:28.111 24278-24278/com.example.demo I/SecondActivity: onPause
2020-12-14 12:38:28.117 24278-24278/com.example.demo I/MainActivity: onStart
2020-12-14 12:38:28.118 24278-24278/com.example.demo I/MainActivity: onResume
2020-12-14 12:38:38.153 24278-24278/com.example.demo I/SecondActivity: onStop
2020-12-14 12:38:38.155 24278-24278/com.example.demo I/SecondActivity: onDestroy

demo的log也显示了在加上动画以后,确实onStop和onDestroy就会被延迟执行,而且试了多次,发现每次延迟的时间都是10s左右。所以猜测一定存在某种定时执行onStop操作的场景,不然不可能每次都这么凑巧。

既然踩到坑,那么我就得想办法搞清楚为什么会出现这种情况。所以还是跟以前一样,查阅Android源码一探究竟。


finish()操作

    /**
     * Finishes the current activity and specifies whether to remove the task associated with this
     * activity.
     */
    @UnsupportedAppUsage
    private void finish(int finishTask) {
        if (mParent == null) {
            int resultCode;
            Intent resultData;
            synchronized (this) {
                resultCode = mResultCode;
                resultData = mResultData;
            }
            if (false) Log.v(TAG, "Finishing self: token=" + mToken);
            try {
                if (resultData != null) {
                    resultData.prepareToLeaveProcess(this);
                }
                if (ActivityTaskManager.getService()
                        .finishActivity(mToken, resultCode, resultData, finishTask)) {
                    mFinished = true;
                }
            } catch (RemoteException e) {
                // Empty
            }
        } else {
            mParent.finishFromChild(this);
        }

        // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must
        // be restored now.
        if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {
            getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,
                    mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));
        }
    }

    /**
     * Call this when your activity is done and should be closed.  The
     * ActivityResult is propagated back to whoever launched you via
     * onActivityResult().
     */
    public void finish() {
        finish(DONT_FINISH_TASK_WITH_ACTIVITY);
    }

Activity的finish方法会调用自身带有参数的finish方法,然后通过Binder会执行ActivityTaskManagerService的finishActivity方法。

    @Override
    public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,
            int finishTask) {
        ...代码省略...

        synchronized (mGlobalLock) {
            ...代码省略...
            try {
                boolean res;
                final boolean finishWithRootActivity =
                        finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;
                if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY
                        || (finishWithRootActivity && r == rootR)) {
                    ...代码省略
                } 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);
            }
        }
    }

由于finishTask是DONT_FINISH_TASK_WITH_ACTIVITY类型,所以会走else分支,tr.getStack()得到的是ActivityStack对象,所以接下来执行的就是ActivityStack的requestFinishActivityLocked方法。

       /**
     * @return Returns true if this activity has been removed from the history
     * list, or false if it is still in the list and will be removed later.
     */
    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 {
            //这个方法是为了将finishing设置为true
            r.makeFinishingLocked();
            final TaskRecord task = r.getTaskRecord();
            EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,
                    r.mUserId, 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);
                }
            }
            //停止按键的事件分发
            r.pauseKeyDispatchingLocked();

            adjustFocusedActivityStack(r, "finishActivity");
            //检查是否有设置ActivityResult,如果存在则加入列表中
            finishActivityResultsLocked(r, resultCode, resultData);

            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
            final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
            //当前页面处于Resume状态,所以会进入此分支
            if (mResumedActivity == r) {
                if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                        "Prepare close transition: finishing " + r);
                if (endTask) {
                    mService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
                            task.getTaskInfo());
                }
                getDisplay().mDisplayContent.prepareAppTransition(transit, false);

                // Tell window manager to prepare for this one to be removed.
                r.setVisibility(false);

                if (mPausingActivity == null) {
                    if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish needs to pause: " + r);
                    if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,
                            "finish() => pause with userLeaving=false");
                     //当前页面还没有进入pause状态,所以会调用此方法
                    startPausingLocked(false, false, null, pauseImmediately);
                }

                if (endTask) {
                    mService.getLockTaskController().clearLockedTask(task);
                }
            } else if (!r.isState(PAUSING)) {
                // If the activity is PAUSING, we will complete the finish once
                // it is done pausing; else we can just directly finish it here.
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
                if (r.visible) {
                    prepareActivityHideTransitionAnimation(r, transit);
                }

                final int finishMode = (r.visible || r.nowVisible) ? FINISH_AFTER_VISIBLE
                        : FINISH_AFTER_PAUSE;
                final boolean removedActivity = finishCurrentActivityLocked(r, finishMode, oomAdj,
                        "finishActivityLocked") == null;

                // The following code is an optimization. When the last non-task overlay activity
                // is removed from the task, we remove the entire task from the stack. However,
                // since that is done after the scheduled destroy callback from the activity, that
                // call to change the visibility of the task overlay activities would be out of
                // sync with the activitiy visibility being set for this finishing activity above.
                // In this case, we can set the visibility of all the task overlay activities when
                // we detect the last one is finishing to keep them in sync.
                if (task.onlyHasTaskOverlayActivities(true /* excludeFinishing */)) {
                    for (ActivityRecord taskOverlay : task.mActivities) {
                        if (!taskOverlay.mTaskOverlay) {
                            continue;
                        }
                        prepareActivityHideTransitionAnimation(taskOverlay, transit);
                    }
                }
                return removedActivity;
            } else {
                if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + r);
            }

            return false;
        } finally {
            mWindowManager.continueSurfaceLayout();
        }
    }
  1. 设置当前ActivityRecord为finishing状态
  2. 停止按键的事件分发
  3. 检查ActivityResult状态,存在的话就加入到列表中
  4. 执行startPausingLocked方法。
    final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
            ActivityRecord resuming, boolean pauseImmediately) {
        ...代码省略...
        if (prev.attachedToProcess()) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLogTags.writeAmPauseActivity(prev.mUserId, System.identityHashCode(prev),
                        prev.shortComponentName, "userLeaving=" + userLeaving);

                mService.getLifecycleManager().scheduleTransaction(prev.app.getThread(),
                        prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
                                prev.configChangeFlags, pauseImmediately));
            } 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;
            }
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
        ...代码省略...

    }

这里面有一大段代码都需要管,重点关注里面的mService.getLifecycleManager().scheduleTransaction这个方法。在这篇文章里面已经了解过了在Android9.0之后生命周期交给了LifecycleItem处理,是通过TransactionExecutor进行调度的。
这段代码最终会执行ActivityThread里面的下面这段处理:

ActivityThread.java

                case EXECUTE_TRANSACTION:
                    final ClientTransaction transaction = (ClientTransaction) msg.obj;
                    mTransactionExecutor.execute(transaction);
                    if (isSystem()) {
                        // Client transactions inside system process are recycled on the client side
                        // instead of ClientLifecycleManager to avoid being cleared before this
                        // message is handled.
                        transaction.recycle();
                    }
                    // TODO(lifecycler): Recycle locally scheduled transactions.
                    break;

TransactionExecutor.java

   private void executeLifecycleState(ClientTransaction transaction) {
        final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
        if (lifecycleItem == null) {
            // No lifecycle request, return early.
            return;
        }

        final IBinder token = transaction.getActivityToken();
        final ActivityClientRecord r = mTransactionHandler.getActivityClient(token);
        if (DEBUG_RESOLVER) {
            Slog.d(TAG, tId(transaction) + "Resolving lifecycle state: "
                    + lifecycleItem + " for activity: "
                    + getShortActivityName(token, mTransactionHandler));
        }

        if (r == null) {
            // Ignore requests for non-existent client records for now.
            return;
        }

        // Cycle to the state right before the final requested state.
        cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);

        // Execute the final transition with proper parameters.
        lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
        lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
    }

我们是执行了startPausingLocked方法, 那么此处最后自然会执行到PauseActivityItem的execute方法里面去的。execute方法就是简单的做了pause的生命周期方法,重点关注下postExecute方法,这里简单梳理下postExecute的调用链

  • PauseActivityItem#poseExecute()
  • ActivityTaskManagerService#activityPaused()
  • ActivityStack#activityPausedLocked()
  • ActivityStack#finishCurrentActivityLocked()
  • RootActivityContainer#resumeFocusedStacksTopActivities()
  • ActivityStack#resumeTopActivityUncheckedLocked()
  • ActivityStack#resumeTopActivityInnerLocked()
@GuardedBy("mService")
    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ...代码省略...
        if (next.attachedToProcess()) {
            ...代码省略...
            try {
                final ClientTransaction transaction =
                        ClientTransaction.obtain(next.app.getThread(), next.appToken);
                // Deliver all pending results.
                ArrayList<ResultInfo> a = next.results;
                if (a != null) {
                    final int N = a.size();
                    if (!next.finishing && N > 0) {
                        if (DEBUG_RESULTS) Slog.v(TAG_RESULTS,
                                "Delivering results to " + next + ": " + a);
                        transaction.addCallback(ActivityResultItem.obtain(a));
                    }
                }

                if (next.newIntents != null) {
                    transaction.addCallback(
                            NewIntentItem.obtain(next.newIntents, true /* resume */));
                }

                // Well the app will no longer be stopped.
                // Clear app token stopped state in window manager if needed.
                next.notifyAppResumed(next.stopped);

                EventLog.writeEvent(EventLogTags.AM_RESUME_ACTIVITY, next.mUserId,
                        System.identityHashCode(next), next.getTaskRecord().taskId,
                        next.shortComponentName);

                next.sleeping = false;
                mService.getAppWarningsLocked().onResumeActivity(next);
                next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
                next.clearOptionsLocked();
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                getDisplay().mDisplayContent.isNextTransitionForward()));
                mService.getLifecycleManager().scheduleTransaction(transaction);

                if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Resumed "
                        + next);
            } catch (Exception e) {
                ...代码省略...
                return true;
            }

            // From this point on, if something goes wrong there is no way
            // to recover the activity.
            try {
                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);
                return true;
            }
        } else {
            // Whoops, need to restart this activity!
            if (!next.hasBeenLaunched) {
                next.hasBeenLaunched = true;
            } else {
                if (SHOW_APP_STARTING_PREVIEW) {
                    next.showStartingWindow(null /* prev */, false /* newTask */,
                            false /* taskSwich */);
                }
                if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
            }
            if (DEBUG_STATES) Slog.d(TAG_STATES, "resumeTopActivityLocked: Restarting " + next);
            mStackSupervisor.startSpecificActivityLocked(next, true, true);
        }

        return true;
    }

这段代码在浅谈APP的回收和重启机制这篇文章中有提及,next.attachedToProcess()可以判断next页面是否已经被系统回收,如果是false就代表页面被回收了走else分支,调用StackSupervisor的startSpecificActivityLocked重启页面。如果是true,那么就会通过TransactionExecutor调度执行ResumeActivityItem的execute方法,执行前一个页面的onResume方法。
逻辑处理完以后接下来有这么一段代码:

            // From this point on, if something goes wrong there is no way
            // to recover the activity.
            try {
                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);
                return true;
            }

我们看下completeResumeLocked的逻辑:

    void completeResumeLocked() {
        ...代码省略...
        // Schedule an idle timeout in case the app doesn't do it for us.
        mStackSupervisor.scheduleIdleTimeoutLocked(this);
        ...代码省略...

    }
    void scheduleIdleTimeoutLocked(ActivityRecord next) {
        if (DEBUG_IDLE) Slog.d(TAG_IDLE,
                "scheduleIdleTimeoutLocked: Callers=" + Debug.getCallers(4));
        Message msg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG, next);
        mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);
    }

主要就是发送一个延时的message,message主要就是用了执行Activity的onStop和onDestroy生命周期,而IDLE_TIMEOUT正是10s。

当前页面的Activity执行了finish操作,并且onStop已经调用,然后上一个页面的onResume方法也已经调用。至此finish相关的操作逻辑告一段落,但是似乎没有看到执行onStop和onDestroy地方(只有一个延迟10s执行的代码)

handleResumeActivity的处理

   @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...代码省略...
        
        //这段代码最终会执行Activity的onResume方法 
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

        ...代码省略...
        
        if (r.window == null && !a.mFinished && willBeVisible) {
            //这个判断条件代表当前页面是第一次进入,条件里面的处理就是初始化ViewRootImpl,将View添加在Window上面,执行View的第一次绘制操作
            r.window = r.activity.getWindow();
            View decor = r.window.getDecorView();
            decor.setVisibility(View.INVISIBLE);
            ViewManager wm = a.getWindowManager();
            WindowManager.LayoutParams l = r.window.getAttributes();
            a.mDecor = decor;
            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (r.mPreserveWindow) {
                a.mWindowAdded = true;
                r.mPreserveWindow = false;
                // Normally the ViewRoot sets up callbacks with the Activity
                // in addView->ViewRootImpl#setView. If we are instead reusing
                // the decor view we have to notify the view root that the
                // callbacks may have changed.
                ViewRootImpl impl = decor.getViewRootImpl();
                if (impl != null) {
                    impl.notifyChildRebuilt();
                }
            }
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                } else {
                    // The activity will get a callback for this {@link LayoutParams} change
                    // earlier. However, at that time the decor will not be set (this is set
                    // in this method), so no action will be taken. This call ensures the
                    // callback occurs with the decor set.
                    a.onWindowAttributesChanged(l);
                }
            }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
        } else if (!willBeVisible) {
            if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
            r.hideForNow = true;
        }

        ...代码省略...

        r.nextIdle = mNewActivities;
        mNewActivities = r;
        if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
        Looper.myQueue().addIdleHandler(new Idler());
    }

handleResumeActivity的过程自然也不是本文的重点,重点关注最后一句话Looper.myQueue().addIdleHandler(new Idler()),这句话什么作用呢?这里就要重点看下addIdleHandler方法到底添加了什么东西。

MessageQueue.java

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

很简单的代码,只是将IdleHandler添加到mIdleHandlers的列表中,那么什么时候会使用这个对象呢。查看了mIdleHandlers的调用地方


6d696b65b49c58db3e2553728f7f92fc.jpg

发现除了添加和删除IdleHandler对象以外只有MessageQueue的next方法里面有调用的地方。

   Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // 获取当前时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //target为空的情况下,才会进入此条件
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                        //遍历messageQueue里面的所有消息,如果存在message是异步的,那么返回给调用者调用。如果不存在异步消息,那么等遍历完退出循环
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                         //when主要是postDelay设置的,通过postDelay可以延迟执行,如果当前时间小于when,那么该消息不会被执行。
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

这里主要讲下next的大致流程

  1. 获取当前时间
  2. 判断当前message的target对象是否为空,如果是空的会进入if条件中(什么情况下message的target为空呢,答案是调用了postSyncBarrier方法,什么作用呢?主要是ViewRootImpl做同步屏障用的,为了可以让UI事件优先被处理,会设置一个同步屏障,然后发送异步消息处理UI。最大可能保证UI的流畅性)
  3. 获取异步消息(如果存在)
  4. 判断当前消息的when参数是否大于now(now为当前时间,如果执行postDelay的操作,when是有可能会大于now的,那么这个时候消息将不会执行)
  5. 如果当前消息需要立刻执行,将消息从链表中取出,然后设置标志位,交给Looper执行dispatch方法
    以上就是消息的主要处理流程。

那么一旦MessageQueue里面的message没了,会怎么样呢?接下来就会从mIdleHandlers获取idleHandler对象,然后调用queueIdle方法。这里的IdleHandler对象其实就是ActivityThread的Idle类的实例化对象

    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;
                IActivityTaskManager am = ActivityTaskManager.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 {
                            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);
            }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            applyPendingProcessState();
            return false;
        }
    }

这里面获取了ActivityTaskManagerService对象,然后调用了activityIdle方法。下面给出简单的调用链

  • ActivityTaskManagerService#activityIdle()
  • ActivityStackSupervisor#activityIdleInternalLocked()
    @GuardedBy("mService")
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            boolean processPausingActivities, Configuration config) {
        if (DEBUG_ALL) Slog.v(TAG, "Activity idle: " + token);

        ArrayList<ActivityRecord> finishes = null;
        ArrayList<UserState> startingUsers = null;
        int NS = 0;
        int NF = 0;
        boolean booting = false;
        boolean activityRemoved = false;

       ActivityRecord r = ActivityRecord.forTokenLocked(token);
        if (r != null) {
            if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
                    + Debug.getCallers(4));
            mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
            ...代码省略...
        }
        ...代码省略...

        // Atomically retrieve all of the other things to do.
        final ArrayList<ActivityRecord> stops = processStoppingActivitiesLocked(r,
                true /* remove */, processPausingActivities);
        NS = stops != null ? stops.size() : 0;
        ...代码省略...

        // Stop any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NS; i++) {
            r = stops.get(i);
            final ActivityStack stack = r.getActivityStack();
            if (stack != null) {
                if (r.finishing) {
                    //之前调用finish的时候会将此变量置为true
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
                            "activityIdleInternalLocked");
                } else {
                    stack.stopActivityLocked(r);
                }
            }
        }

        ...代码省略...

        return r;
    }

ActivityStackSupervisor#activityIdleInternalLocked首先获取对应的ActivityRecord,然后移除掉对应的IDLE_TIMEOUT_MSG(这个Message正是之前延时10s执行的message)。然后获取到即将进入stop的Activity,判断当前Activity是否finishing,我们当前页面执行了finish方法,自然是true,所以会调用ActivityStack#finishCurrentActivityLocked()方法。这个方法看上去是否很熟悉?没错,之前在执行startPauseLocked的时候也调用了这个方法,只不过那个时候传入的第二个参数值是FINISH_AFTER_VISIBLE,而现在传入的第二个参数值是FINISH_IMMEDIATELY。那么这个值什么作用呢,进去再看下:

    final ActivityRecord finishCurrentActivityLocked(ActivityRecord r, int mode, boolean oomAdj,
            String reason) {
        ...代码省略...

        if (mode == FINISH_IMMEDIATELY
                || (prevState == PAUSED
                    && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
                || finishingInNonFocusedStackOrNoRunning
                || prevState == STOPPING
                || prevState == STOPPED
                || prevState == ActivityState.INITIALIZING) {
            r.makeFinishingLocked();
            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm:" + reason);

            if (finishingInNonFocusedStackOrNoRunning) {
                // Finishing activity that was in paused state and it was in not currently focused
                // stack, need to make something visible in its place. Also if the display does not
                // have running activity, the configuration may need to be updated for restoring
                // original orientation of the display.
                mRootActivityContainer.ensureVisibilityAndConfig(next, mDisplayId,
                        false /* markFrozenIfConfigChanged */, true /* deferResume */);
            }
            if (activityRemoved) {
                mRootActivityContainer.resumeFocusedStacksTopActivities();
            }
            if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
                    "destroyActivityLocked: finishCurrentActivityLocked r=" + r +
                    " destroy returned removed=" + activityRemoved);
            return activityRemoved ? null : r;
        }

        // Need to go through the full pause cycle to get this
        // activity into the stopped state and then finish it.
        if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
        mStackSupervisor.mFinishingActivities.add(r);
        r.resumeKeyDispatchingLocked();
        mRootActivityContainer.resumeFocusedStacksTopActivities();
        // If activity was not paused at this point - explicitly pause it to start finishing
        // process. Finishing will be completed once it reports pause back.
        if (r.isState(RESUMED) && mPausingActivity != null) {
            startPausingLocked(false /* userLeaving */, false /* uiSleeping */, next /* resuming */,
                    false /* dontWait */);
        }
        return r;
    }

当mode是FINISH_IMMEDIATELY时,会进入上面代码的if条件中,执行destroyActivityLocked的方法。至于里面的细节不是本文终点,不在继续探究。
至此Activity的onStop方法和onDestroy方法就执行完毕了。从上面代码可以看出正常情况下,在执行到此方法的时候,会移除掉10s delay的message,onStop和onDestroy会在预期范围内执行。


所以如果Activity延迟10s执行,应该是ActivityThread的Idler#queueIdle方法没有被执行,什么情况下不会被执行呢?我们重新看下MessageQueue的next()方法

    @UnsupportedAppUsage
    Message next() {
        ...代码省略...
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                ...代码省略...
                if (msg != null) {
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        ...代码省略...
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                ...代码省略...

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            ...代码省略...
        }
    }

如果每次遍历的时候都能拿到message,那么idler的queueIdle就永远也执行不了了。为什么messageQueue里面的message取不完呢。就只能去看Animation到底是怎么运行的了。

Animation的执行流程

首先从View的startAnimation开始说起:

    public void startAnimation(Animation animation) {
        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
        setAnimation(animation);
        invalidateParentCaches();
        invalidate(true);
    }

发现View启动了Animation以后会执行invalidate方法。下面是invalidate的调用链

  • View#invalidate()
  • View#invalidateInternal()
  • ViewGroup#invalidateChild()
  • ViewGroup#invalidateChildInParent()
  • ViewRootImpl#invalidateChildInParent()
  • ViewRootImpl#invalidateRectOnScreen()
  • ViewRootImpl#scheduleTraversals()
    最终会调用到ViewRootImpl的scheduleTraversals方法中
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

这里设置了同步屏障,然后发射异步消息目的就是为了让UI消息第一时间能够得到处理,提高用户体验。此处post一个message消息,后续逻辑不是本文重点,不做详细说明。最后会调用mTraversalRunnable的run方法

    void doTraversal() {
        if (mTraversalScheduled) {
            //此处做了屏障,目的是使得一次Vsync信号只能绘制一次,
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
            //执行performTraversals方法
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

performTraversals相信都比较熟悉了,这个方法最终会调用performDraw方法然后最终执行ViewGroup的dispatchdraw方法,r然后调用ViewGroup的drawChild方法,最后执行到View的draw方法,这里的draw方法携带有三个参数

ViewGroup.java

boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
        final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
        ...代码省略...
        if (a != null) {
            more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
            concatMatrix = a.willChangeTransformationMatrix();
            if (concatMatrix) {
                mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            transformToApply = parent.getChildTransformation();
        } else {
            if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) {
                // No longer animating: clear out old animation matrix
                mRenderNode.setAnimationMatrix(null);
                mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
            }
            if (!drawingWithRenderNode
                    && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
                final Transformation t = parent.getChildTransformation();
                final boolean hasTransform = parent.getChildStaticTransformation(this, t);
                if (hasTransform) {
                    final int transformType = t.getTransformationType();
                    transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
                    concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
                }
            }
        }

        ...代码省略...

        return more;
    }

这个方法非常长,我们只需要关心applyLegacyAnimation这个方法即可

    private boolean applyLegacyAnimation(ViewGroup parent, long drawingTime,
            Animation a, boolean scalingRequired) {
        ...代码省略...
        boolean more = a.getTransformation(drawingTime, t, 1f);
        ...代码省略...
        if (more) {
            if (!a.willChangeBounds()) {
               ...代码省略...
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                        invalidationTransform);

                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f));
            }
        }
        return more;
    }

首先我们来看下getTransformation方法,这个方法会返回一个boolean值,那么我们看下到底返回的是什么值。

    public boolean getTransformation(long currentTime, Transformation outTransformation) {
       ...代码省略...
        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }
        final boolean expired = normalizedTime >= 1.0f || isCanceled();
        mMore = !expired;
        ...代码省略...
        if (expired) {
            if (mRepeatCount == mRepeated || isCanceled()) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }
        return mMore;
    }

其他处理都是本篇重点,我们只需要关注上面这个逻辑,返回值mMore跟expired有关,而expired跟后面两个判断有关,由于我们的动画并没有调用cancel方法, 所以isCanceled必然是false。normalizedTime表示当前的进度,举个栗子:如果duration是200ms,mStartTime是0ms,currentTime是100ms,那么最后获取到的normalizedTime就是0.5f。
所以这个时候有两种情况

  1. 动画还在一个duration之内,那么normalizedTime >= 1.0f为false,这个时候expired为false,那么mMore就为true。我们暂且不管mMore的作用,后面会说明
  2. 动画已经超过了一个duration(即一次动画执行完毕),那么normalizedTime >= 1.0f为true,这个时候expired为true,而mMore为false。这个时候下面这段逻辑就会进去:
if (expired) {
            if (mRepeatCount == mRepeated || isCanceled()) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }

又因为本地动画设置的是mRepeat为INFINITE即-1,所以mRepeatCount == mRepeated不可能会相等,isCanceled又是false,所以会进入else分支,最后发现mMore依然会是true;

综上所述,当前代码下面,mMore永远都为true,接下来看下true的作用,我们回到View的applyLegacyAnimation方法中,看下面这段代码:

        if (more) {
            if (!a.willChangeBounds()) {
                if ((flags & (ViewGroup.FLAG_OPTIMIZE_INVALIDATE | ViewGroup.FLAG_ANIMATION_DONE)) ==
                        ViewGroup.FLAG_OPTIMIZE_INVALIDATE) {
                    parent.mGroupFlags |= ViewGroup.FLAG_INVALIDATE_REQUIRED;
                } else if ((flags & ViewGroup.FLAG_INVALIDATE_REQUIRED) == 0) {
                    // The child need to draw an animation, potentially offscreen, so
                    // make sure we do not cancel invalidate requests
                    parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
                    parent.invalidate(mLeft, mTop, mRight, mBottom);
                }
            } else {
                if (parent.mInvalidateRegion == null) {
                    parent.mInvalidateRegion = new RectF();
                }
                final RectF region = parent.mInvalidateRegion;
                a.getInvalidateRegion(0, 0, mRight - mLeft, mBottom - mTop, region,
                        invalidationTransform);

                // The child need to draw an animation, potentially offscreen, so
                // make sure we do not cancel invalidate requests
                parent.mPrivateFlags |= PFLAG_DRAW_ANIMATION;

                final int left = mLeft + (int) region.left;
                final int top = mTop + (int) region.top;
                parent.invalidate(left, top, left + (int) (region.width() + .5f),
                        top + (int) (region.height() + .5f));
            }
        }

当more为true的时候,会进入if条件中,因为Animation#willChangeBounds为true,所以会进入else分支,后面会执行parent.invalidate方法,这个其实就是View的invalidate方法。而invalidate又会发一次message给messageQueue。导致MessageQueue永远不可能为空。下面给出一张Animation的流程图


Animation执行流程.jpg

总结

这里我们把整理的流程梳理一遍

  1. 上一个页面执行了Animation动画,由于动画是无限循环的,所以Animation会无限循环的往MessageQueue发送绘制UI的消息。
  2. 当前页面执行finish操作,Activity进入onPause状态
  3. 然后寻找下一个即将resume的Activity,进入resume状态
  4. 发送一个延迟10s的消息进入messagequeue中,这个消息用来执行Activity的onStop和onDestroy操作
  5. 发送一个Idler对象,旨在MessageQueue空闲的时候执行Activity的onStop和onDestroy操作,并且移除掉第4步发送的延迟消息
  6. 由于MessageQueue一直有消息在执行,所以Idler对象没有执行的时机,10s之后,延迟的消息会执行onStop操作。


    完整流程.jpg

解决方案

讲完了原因,下面就给出三个解决方案:

  1. 在页面进入onPause的时候可以暂停Animation,然后在onResume的时候发送一个延迟的消息执行Animation,这样Idler就会有空隙执行Activity的onStop生命周期。不过由于这种方案不可靠,因为手机性能的原因,所以有可能存在发送的延迟消息已经开始执行了,Idler对象仍然还没有被post到messagequeue当中。
  2. Animation替换成ValueAnimation,属性动画不会导致Activity的生命周期延迟执行(具体原因本文不做详细分析, 后期有时间可以详细研究),不过这种方案也只是解决了本文中的场景,没办法处理所有导致Activity生命周期延迟10s执行的操作。
  3. 我们经常会在onStop和onDestroy方法中做资源释放的操作,但是由于这个原因的存在可能会出现资源释放不及时导致的bug,那我们可以在onPause的时候判断当前finishing状态,如果是true,证明Activity即将关闭,那么可以直接释放资源。这种做法也比较合理,可以适用于各种场景。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容