踩坑之路:Activity執(zhí)行finish以后onStop方法10s以后才調(diào)用

背景

某天妖枚,測試提了一個(gè)bug廷臼,說當(dāng)前頁面關(guān)閉了以后回到了上一個(gè)頁面,但是對應(yīng)的音樂并沒有立刻停止绝页,而是過了一段時(shí)間才停止中剩。于是翻閱了一下代碼:

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

mAudioTool.pausePlayAudio方法是停止播放音頻的代碼,似乎并沒有什么問題抒寂。而且這段代碼已經(jīng)運(yùn)行了好久,之前測試也是通過的掠剑,為何這個(gè)版本會(huì)出現(xiàn)這個(gè)問題屈芜?


所以首先我debug一下當(dāng)前頁面的onStop方法,結(jié)果發(fā)現(xiàn)頁面關(guān)閉的時(shí)候onStop方法并沒有被執(zhí)行朴译,然后過了差不多10s左右再被執(zhí)行井佑,感覺又是踩到了某個(gè)坑里面去了,仔細(xì)回想了最近改了的代碼眠寿,問題可能出在了上一個(gè)頁面的下面這段代碼:

    /**
     * 開始抖動(dòng)
     */
    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()) {
            //當(dāng)前動(dòng)畫已經(jīng)開始并且沒有結(jié)束
            return
        }
        //從左向右
        mRotateAnim!!.duration = 50
        mRotateAnim!!.repeatMode = Animation.REVERSE
        mRotateAnim!!.repeatCount = Animation.INFINITE
        val smallAnimationSet = AnimationSet(false)
        smallAnimationSet.addAnimation(mRotateAnim)
        cl_wrong_book.startAnimation(smallAnimationSet)
    }

由于設(shè)置的repeatCount是INFINITE躬翁,所以動(dòng)畫是一直在執(zhí)行中的。
不過還是有點(diǎn)納悶盯拱,動(dòng)畫跟Activity生命周期有啥關(guān)系盒发?為了印證下到底是不是Animation影響的當(dāng)前頁面生命周期調(diào)用異常例嘱,于是將這段代碼進(jìn)行了刪除操作。結(jié)果Activity的onStop生命周期還真的就正常執(zhí)行了宁舰。然后自己又做了一個(gè)demo拼卵,來查看下onStop的執(zhí)行時(shí)間。
(這段是沒有動(dòng)畫的)

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

(這段是加上動(dòng)畫的)

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也顯示了在加上動(dòng)畫以后蛮艰,確實(shí)onStop和onDestroy就會(huì)被延遲執(zhí)行腋腮,而且試了多次,發(fā)現(xiàn)每次延遲的時(shí)間都是10s左右壤蚜。所以猜測一定存在某種定時(shí)執(zhí)行onStop操作的場景即寡,不然不可能每次都這么湊巧。

既然踩到坑袜刷,那么我就得想辦法搞清楚為什么會(huì)出現(xiàn)這種情況聪富。所以還是跟以前一樣,查閱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方法會(huì)調(diào)用自身帶有參數(shù)的finish方法善涨,然后通過Binder會(huì)執(zhí)行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類型草则,所以會(huì)走else分支钢拧,tr.getStack()得到的是ActivityStack對象,所以接下來執(zhí)行的就是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) {
            //這個(gè)判斷條件是為了防止多次進(jìn)入源内,做了一道屏障
            Slog.w(TAG, "Duplicate finish request for " + r);
            return false;
        }

        mWindowManager.deferSurfaceLayout();
        try {
            //這個(gè)方法是為了將finishing設(shè)置為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);
                }
            }
            //停止按鍵的事件分發(fā)
            r.pauseKeyDispatchingLocked();

            adjustFocusedActivityStack(r, "finishActivity");
            //檢查是否有設(shè)置ActivityResult,如果存在則加入列表中
            finishActivityResultsLocked(r, resultCode, resultData);

            final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
            final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
            //當(dāng)前頁面處于Resume狀態(tài)份殿,所以會(huì)進(jìn)入此分支
            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");
                     //當(dāng)前頁面還沒有進(jìn)入pause狀態(tài)膜钓,所以會(huì)調(diào)用此方法
                    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. 設(shè)置當(dāng)前ActivityRecord為finishing狀態(tài)
  2. 停止按鍵的事件分發(fā)
  3. 檢查ActivityResult狀態(tài),存在的話就加入到列表中
  4. 執(zhí)行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;
        }
        ...代碼省略...

    }

這里面有一大段代碼都需要管颂斜,重點(diǎn)關(guān)注里面的mService.getLifecycleManager().scheduleTransaction這個(gè)方法。在這篇文章里面已經(jīng)了解過了在Android9.0之后生命周期交給了LifecycleItem處理拾枣,是通過TransactionExecutor進(jìn)行調(diào)度的沃疮。
這段代碼最終會(huì)執(zhí)行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);
    }

我們是執(zhí)行了startPausingLocked方法, 那么此處最后自然會(huì)執(zhí)行到PauseActivityItem的execute方法里面去的梅肤。execute方法就是簡單的做了pause的生命周期方法司蔬,重點(diǎn)關(guān)注下postExecute方法,這里簡單梳理下postExecute的調(diào)用鏈

  • 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的回收和重啟機(jī)制這篇文章中有提及,next.attachedToProcess()可以判斷next頁面是否已經(jīng)被系統(tǒng)回收姨蝴,如果是false就代表頁面被回收了走else分支俊啼,調(diào)用StackSupervisor的startSpecificActivityLocked重啟頁面。如果是true左医,那么就會(huì)通過TransactionExecutor調(diào)度執(zhí)行ResumeActivityItem的execute方法授帕,執(zhí)行前一個(gè)頁面的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);
    }

主要就是發(fā)送一個(gè)延時(shí)的message,message主要就是用了執(zhí)行Activity的onStop和onDestroy生命周期豪墅,而IDLE_TIMEOUT正是10s泉手。

當(dāng)前頁面的Activity執(zhí)行了finish操作,并且onStop已經(jīng)調(diào)用偶器,然后上一個(gè)頁面的onResume方法也已經(jīng)調(diào)用斩萌。至此finish相關(guān)的操作邏輯告一段落,但是似乎沒有看到執(zhí)行onStop和onDestroy地方(只有一個(gè)延遲10s執(zhí)行的代碼)

handleResumeActivity的處理

   @Override
    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        ...代碼省略...
        
        //這段代碼最終會(huì)執(zhí)行Activity的onResume方法 
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);

        ...代碼省略...
        
        if (r.window == null && !a.mFinished && willBeVisible) {
            //這個(gè)判斷條件代表當(dāng)前頁面是第一次進(jìn)入屏轰,條件里面的處理就是初始化ViewRootImpl颊郎,將View添加在Window上面,執(zhí)行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的過程自然也不是本文的重點(diǎn)霎苗,重點(diǎn)關(guān)注最后一句話Looper.myQueue().addIdleHandler(new Idler())姆吭,這句話什么作用呢?這里就要重點(diǎn)看下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的列表中,那么什么時(shí)候會(huì)使用這個(gè)對象呢厘擂。查看了mIdleHandlers的調(diào)用地方


6d696b65b49c58db3e2553728f7f92fc.jpg

發(fā)現(xiàn)除了添加和刪除IdleHandler對象以外只有MessageQueue的next方法里面有調(diào)用的地方昆淡。

   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) {
                // 獲取當(dāng)前時(shí)間
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                    //target為空的情況下,才會(huì)進(jìn)入此條件
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                        //遍歷messageQueue里面的所有消息刽严,如果存在message是異步的昂灵,那么返回給調(diào)用者調(diào)用。如果不存在異步消息舞萄,那么等遍歷完退出循環(huán)
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                         //when主要是postDelay設(shè)置的眨补,通過postDelay可以延遲執(zhí)行,如果當(dāng)前時(shí)間小于when倒脓,那么該消息不會(huì)被執(zhí)行撑螺。
                        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. 獲取當(dāng)前時(shí)間
  2. 判斷當(dāng)前message的target對象是否為空,如果是空的會(huì)進(jìn)入if條件中(什么情況下message的target為空呢崎弃,答案是調(diào)用了postSyncBarrier方法甘晤,什么作用呢?主要是ViewRootImpl做同步屏障用的吊履,為了可以讓UI事件優(yōu)先被處理,會(huì)設(shè)置一個(gè)同步屏障调鬓,然后發(fā)送異步消息處理UI艇炎。最大可能保證UI的流暢性)
  3. 獲取異步消息(如果存在)
  4. 判斷當(dāng)前消息的when參數(shù)是否大于now(now為當(dāng)前時(shí)間,如果執(zhí)行postDelay的操作腾窝,when是有可能會(huì)大于now的缀踪,那么這個(gè)時(shí)候消息將不會(huì)執(zhí)行)
  5. 如果當(dāng)前消息需要立刻執(zhí)行居砖,將消息從鏈表中取出,然后設(shè)置標(biāo)志位驴娃,交給Looper執(zhí)行dispatch方法
    以上就是消息的主要處理流程奏候。

那么一旦MessageQueue里面的message沒了,會(huì)怎么樣呢唇敞?接下來就會(huì)從mIdleHandlers獲取idleHandler對象蔗草,然后調(diào)用queueIdle方法。這里的IdleHandler對象其實(shí)就是ActivityThread的Idle類的實(shí)例化對象

    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對象疆柔,然后調(diào)用了activityIdle方法咒精。下面給出簡單的調(diào)用鏈

  • 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) {
                    //之前調(diào)用finish的時(shí)候會(huì)將此變量置為true
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
                            "activityIdleInternalLocked");
                } else {
                    stack.stopActivityLocked(r);
                }
            }
        }

        ...代碼省略...

        return r;
    }

ActivityStackSupervisor#activityIdleInternalLocked首先獲取對應(yīng)的ActivityRecord,然后移除掉對應(yīng)的IDLE_TIMEOUT_MSG(這個(gè)Message正是之前延時(shí)10s執(zhí)行的message)旷档。然后獲取到即將進(jìn)入stop的Activity模叙,判斷當(dāng)前Activity是否finishing,我們當(dāng)前頁面執(zhí)行了finish方法鞋屈,自然是true范咨,所以會(huì)調(diào)用ActivityStack#finishCurrentActivityLocked()方法。這個(gè)方法看上去是否很熟悉厂庇?沒錯(cuò)渠啊,之前在執(zhí)行startPauseLocked的時(shí)候也調(diào)用了這個(gè)方法,只不過那個(gè)時(shí)候傳入的第二個(gè)參數(shù)值是FINISH_AFTER_VISIBLE宋列,而現(xiàn)在傳入的第二個(gè)參數(shù)值是FINISH_IMMEDIATELY昭抒。那么這個(gè)值什么作用呢,進(jìn)去再看下:

    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;
    }

當(dāng)mode是FINISH_IMMEDIATELY時(shí)炼杖,會(huì)進(jìn)入上面代碼的if條件中灭返,執(zhí)行destroyActivityLocked的方法。至于里面的細(xì)節(jié)不是本文終點(diǎn)坤邪,不在繼續(xù)探究熙含。
至此Activity的onStop方法和onDestroy方法就執(zhí)行完畢了。從上面代碼可以看出正常情況下艇纺,在執(zhí)行到此方法的時(shí)候怎静,會(huì)移除掉10s delay的message,onStop和onDestroy會(huì)在預(yù)期范圍內(nèi)執(zhí)行黔衡。


所以如果Activity延遲10s執(zhí)行蚓聘,應(yīng)該是ActivityThread的Idler#queueIdle方法沒有被執(zhí)行,什么情況下不會(huì)被執(zhí)行呢盟劫?我們重新看下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);
                    }
                }
            }

            ...代碼省略...
        }
    }

如果每次遍歷的時(shí)候都能拿到message夜牡,那么idler的queueIdle就永遠(yuǎn)也執(zhí)行不了了。為什么messageQueue里面的message取不完呢侣签。就只能去看Animation到底是怎么運(yùn)行的了塘装。

Animation的執(zhí)行流程

首先從View的startAnimation開始說起:

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

發(fā)現(xiàn)View啟動(dòng)了Animation以后會(huì)執(zhí)行invalidate方法急迂。下面是invalidate的調(diào)用鏈

  • View#invalidate()
  • View#invalidateInternal()
  • ViewGroup#invalidateChild()
  • ViewGroup#invalidateChildInParent()
  • ViewRootImpl#invalidateChildInParent()
  • ViewRootImpl#invalidateRectOnScreen()
  • ViewRootImpl#scheduleTraversals()
    最終會(huì)調(diào)用到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();
        }
    }

這里設(shè)置了同步屏障,然后發(fā)射異步消息目的就是為了讓UI消息第一時(shí)間能夠得到處理蹦肴,提高用戶體驗(yàn)僚碎。此處post一個(gè)message消息,后續(xù)邏輯不是本文重點(diǎn)阴幌,不做詳細(xì)說明勺阐。最后會(huì)調(diào)用mTraversalRunnable的run方法

    void doTraversal() {
        if (mTraversalScheduled) {
            //此處做了屏障,目的是使得一次Vsync信號(hào)只能繪制一次裂七,
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

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

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

performTraversals相信都比較熟悉了皆看,這個(gè)方法最終會(huì)調(diào)用performDraw方法然后最終執(zhí)行ViewGroup的dispatchdraw方法,r然后調(diào)用ViewGroup的drawChild方法,最后執(zhí)行到View的draw方法背零,這里的draw方法攜帶有三個(gè)參數(shù)

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;
    }

這個(gè)方法非常長腰吟,我們只需要關(guān)心applyLegacyAnimation這個(gè)方法即可

    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方法,這個(gè)方法會(huì)返回一個(gè)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;
    }

其他處理都是本篇重點(diǎn),我們只需要關(guān)注上面這個(gè)邏輯侦镇,返回值mMore跟expired有關(guān)灵疮,而expired跟后面兩個(gè)判斷有關(guān),由于我們的動(dòng)畫并沒有調(diào)用cancel方法壳繁, 所以isCanceled必然是false震捣。normalizedTime表示當(dāng)前的進(jìn)度,舉個(gè)栗子:如果duration是200ms闹炉,mStartTime是0ms蒿赢,currentTime是100ms,那么最后獲取到的normalizedTime就是0.5f渣触。
所以這個(gè)時(shí)候有兩種情況

  1. 動(dòng)畫還在一個(gè)duration之內(nèi)羡棵,那么normalizedTime >= 1.0f為false,這個(gè)時(shí)候expired為false嗅钻,那么mMore就為true皂冰。我們暫且不管mMore的作用,后面會(huì)說明
  2. 動(dòng)畫已經(jīng)超過了一個(gè)duration(即一次動(dòng)畫執(zhí)行完畢)养篓,那么normalizedTime >= 1.0f為true秃流,這個(gè)時(shí)候expired為true,而mMore為false柳弄。這個(gè)時(shí)候下面這段邏輯就會(huì)進(jìn)去:
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();
            }
        }

又因?yàn)楸镜貏?dòng)畫設(shè)置的是mRepeat為INFINITE即-1舶胀,所以mRepeatCount == mRepeated不可能會(huì)相等,isCanceled又是false,所以會(huì)進(jìn)入else分支峻贮,最后發(fā)現(xiàn)mMore依然會(huì)是true;

綜上所述应闯,當(dāng)前代碼下面纤控,mMore永遠(yuǎn)都為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));
            }
        }

當(dāng)more為true的時(shí)候船万,會(huì)進(jìn)入if條件中,因?yàn)锳nimation#willChangeBounds為true骨田,所以會(huì)進(jìn)入else分支耿导,后面會(huì)執(zhí)行parent.invalidate方法,這個(gè)其實(shí)就是View的invalidate方法态贤。而invalidate又會(huì)發(fā)一次message給messageQueue舱呻。導(dǎo)致MessageQueue永遠(yuǎn)不可能為空。下面給出一張Animation的流程圖


Animation執(zhí)行流程.jpg

總結(jié)

這里我們把整理的流程梳理一遍

  1. 上一個(gè)頁面執(zhí)行了Animation動(dòng)畫匕争,由于動(dòng)畫是無限循環(huán)的碟联,所以Animation會(huì)無限循環(huán)的往MessageQueue發(fā)送繪制UI的消息栋操。
  2. 當(dāng)前頁面執(zhí)行finish操作,Activity進(jìn)入onPause狀態(tài)
  3. 然后尋找下一個(gè)即將resume的Activity茬高,進(jìn)入resume狀態(tài)
  4. 發(fā)送一個(gè)延遲10s的消息進(jìn)入messagequeue中,這個(gè)消息用來執(zhí)行Activity的onStop和onDestroy操作
  5. 發(fā)送一個(gè)Idler對象假抄,旨在MessageQueue空閑的時(shí)候執(zhí)行Activity的onStop和onDestroy操作怎栽,并且移除掉第4步發(fā)送的延遲消息
  6. 由于MessageQueue一直有消息在執(zhí)行,所以Idler對象沒有執(zhí)行的時(shí)機(jī)宿饱,10s之后熏瞄,延遲的消息會(huì)執(zhí)行onStop操作。


    完整流程.jpg

解決方案

講完了原因刑棵,下面就給出三個(gè)解決方案:

  1. 在頁面進(jìn)入onPause的時(shí)候可以暫停Animation巴刻,然后在onResume的時(shí)候發(fā)送一個(gè)延遲的消息執(zhí)行Animation,這樣Idler就會(huì)有空隙執(zhí)行Activity的onStop生命周期蛉签。不過由于這種方案不可靠胡陪,因?yàn)槭謾C(jī)性能的原因,所以有可能存在發(fā)送的延遲消息已經(jīng)開始執(zhí)行了碍舍,Idler對象仍然還沒有被post到messagequeue當(dāng)中柠座。
  2. Animation替換成ValueAnimation,屬性動(dòng)畫不會(huì)導(dǎo)致Activity的生命周期延遲執(zhí)行(具體原因本文不做詳細(xì)分析片橡, 后期有時(shí)間可以詳細(xì)研究)妈经,不過這種方案也只是解決了本文中的場景,沒辦法處理所有導(dǎo)致Activity生命周期延遲10s執(zhí)行的操作。
  3. 我們經(jīng)常會(huì)在onStop和onDestroy方法中做資源釋放的操作吹泡,但是由于這個(gè)原因的存在可能會(huì)出現(xiàn)資源釋放不及時(shí)導(dǎo)致的bug骤星,那我們可以在onPause的時(shí)候判斷當(dāng)前finishing狀態(tài),如果是true爆哑,證明Activity即將關(guān)閉洞难,那么可以直接釋放資源。這種做法也比較合理揭朝,可以適用于各種場景队贱。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市潭袱,隨后出現(xiàn)的幾起案子柱嫌,更是在濱河造成了極大的恐慌,老刑警劉巖屯换,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件编丘,死亡現(xiàn)場離奇詭異,居然都是意外死亡彤悔,警方通過查閱死者的電腦和手機(jī)瘪吏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜗巧,“玉大人掌眠,你說我怎么就攤上這事∧灰伲” “怎么了蓝丙?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長望拖。 經(jīng)常有香客問我渺尘,道長,這世上最難降的妖魔是什么说敏? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任鸥跟,我火速辦了婚禮,結(jié)果婚禮上盔沫,老公的妹妹穿的比我還像新娘医咨。我一直安慰自己,他們只是感情好架诞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布拟淮。 她就那樣靜靜地躺著,像睡著了一般谴忧。 火紅的嫁衣襯著肌膚如雪很泊。 梳的紋絲不亂的頭發(fā)上角虫,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機(jī)與錄音委造,去河邊找鬼戳鹅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛昏兆,可吹牛的內(nèi)容都是我干的粉楚。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼亮垫,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伟骨?” 一聲冷哼從身側(cè)響起饮潦,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎携狭,沒想到半個(gè)月后继蜡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡逛腿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年稀并,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片单默。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碘举,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搁廓,到底是詐尸還是另有隱情引颈,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布境蜕,位于F島的核電站蝙场,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏粱年。R本人自食惡果不足惜售滤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望台诗。 院中可真熱鬧完箩,春花似錦、人聲如沸拉队。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氏仗。三九已至吉捶,卻和暖如春夺鲜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背呐舔。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工币励, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人珊拼。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓食呻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親澎现。 傳聞我的和親對象是個(gè)殘疾皇子仅胞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

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