美文网首页Android技术知识Android 开发技术分享
踩坑之路:Activity执行finish以后onStop方法1

踩坑之路:Activity执行finish以后onStop方法1

作者: CDF_cc7d | 来源:发表于2020-12-19 21:33 被阅读0次

    背景

    某天,测试提了一个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即将关闭,那么可以直接释放资源。这种做法也比较合理,可以适用于各种场景。

    相关文章

      网友评论

        本文标题:踩坑之路:Activity执行finish以后onStop方法1

        本文链接:https://www.haomeiwen.com/subject/pczygktx.html