【android】ソースコード解析-onSaveInstanceState実行タイミング


以前はactivityのonSaveInstanceStateメソッドの呼び出しタイミングがよく理解されていませんでしたが、onSaveInstanceStateの次の5つのケースが呼び出されたという記事を見ました.
  • ユーザーが携帯電話のホームキーを押したとき.
  • 携帯電話のホームキーを長押ししたり、メニューキーを押したりした場合.
  • 携帯電話の画面が消えたとき.
  • FirstActivityがSecondActivityを起動すると、FirstActivityが呼び出されます.つまり、新しいActivityを開くと元のActivityが呼び出されます.
  • デフォルトで縦横画面切り替えの場合.

  • 本稿では,ソースコードの観点から,変更方法の実行タイミングを深く理解する.
    AndroidはActivity Threadによってコールバックactivityの各ライフサイクルメソッドonPause/onStopなどを実行することが知られている.
    Activity Threadのソースコードを解析することにより、activityを破棄する場合(戻りキーを押してfinishメソッドを呼び出さない場合)onSaveInstanceStateが実行されることが得られる.具体的な実行タイミングは次のとおりです.
  • targetSdkVersion
  • targetSdkVersion >=(android 11)targetSdkVersion
  • targetSdkValerson>=android 28 onStop後に実行します.

  • 次にソースコードで具体的に分析します
    handlePauseActivity
        @Override
        public void handlePauseActivity(IBinder token, boolean finished, boolean userLeaving,
                int configChanges, PendingTransactionActions pendingActions, String reason) {
            ActivityClientRecord r = mActivities.get(token);
            if (r != null) {
                if (userLeaving) {
                    performUserLeavingActivity(r);
                }
    
                r.activity.mConfigChangeFlags |= configChanges;
                performPauseActivity(r, finished, reason, pendingActions);
    
                // Make sure any pending writes are now committed.
                if (r.isPreHoneycomb()) {
                    QueuedWork.waitToFinish();
                }
                mSomeActivitiesChanged = true;
            }
        }
        /**
         * Pause the activity.
         * @return Saved instance state for pre-Honeycomb apps if it was saved, {@code null} otherwise.
         */
        private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
                PendingTransactionActions pendingActions) {
            if (r.paused) {
                if (r.activity.mFinished) {
                    // If we are finishing, we won't call onResume() in certain cases.
                    // So here we likewise don't want to call onPause() if the activity
                    // isn't resumed.
                    return null;
                }
                RuntimeException e = new RuntimeException(
                        "Performing pause of activity that is not resumed: "
                        + r.intent.getComponent().toShortString());
                Slog.e(TAG, e.getMessage(), e);
            }
            //finished     android native c      ,         activity finish     true
            if (finished) {
                r.activity.mFinished = true;
            }
    
            // Pre-Honeycomb apps always save their state before pausing
            //    android 3.0   
            final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
            if (shouldSaveState) {
                callActivityOnSaveInstanceState(r);
            }
    
            performPauseActivityIfNeeded(r, reason);
    
            // Notify any outstanding on paused listeners
            ArrayList<OnActivityPausedListener> listeners;
            synchronized (mOnPauseListeners) {
                listeners = mOnPauseListeners.remove(r.activity);
            }
            int size = (listeners != null ? listeners.size() : 0);
            for (int i = 0; i < size; i++) {
                listeners.get(i).onPaused(r.activity);
            }
            ...
            ...
            final Bundle oldState = pendingActions != null ? pendingActions.getOldState() : null;
            if (oldState != null) {
                // We need to keep around the original state, in case we need to be created again.
                // But we only do this for pre-Honeycomb apps, which always save their state when
                // pausing, so we can not have them save their state when restarting from a paused
                // state. For HC and later, we want to (and can) let the state be saved as the
                // normal part of stopping the activity.
                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            }
    
            return shouldSaveState ? r.state : null;
        }
    

    handlePauseActivity->performPauseActivityによって、performPauseActivity IfNeededが特定の実行前にcallActivity OnSaveInstanceStateメソッドが呼び出されるのは、shouldSaveStateがtrueである必要がある場合に限られます.
    handleStopActivity
        @Override
        public void handleStopActivity(IBinder token, boolean show, int configChanges,
                PendingTransactionActions pendingActions, boolean finalStateRequest, String reason) {
            final ActivityClientRecord r = mActivities.get(token);
            r.activity.mConfigChangeFlags |= configChanges;
    
            final StopInfo stopInfo = new StopInfo();
            performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,
                    reason);
    
            if (localLOGV) Slog.v(
                TAG, "Finishing stop of " + r + ": show=" + show
                + " win=" + r.window);
    
            updateVisibility(r, show);
    
            // Make sure any pending writes are now committed.
            if (!r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }
    
            stopInfo.setActivity(r);
            stopInfo.setState(r.state);
            stopInfo.setPersistentState(r.persistentState);
            pendingActions.setStopInfo(stopInfo);
            mSomeActivitiesChanged = true;
        }
    ...
    ...
    /**
         * Core implementation of stopping an activity.  Note this is a little
         * tricky because the server's meaning of stop is slightly different
         * than our client -- for the server, stop means to save state and give
         * it the result when it is done, but the window may still be visible.
         * For the client, we want to call onStop()/onStart() to indicate when
         * the activity's UI visibility changes.
         * @param r Target activity client record.
         * @param info Action that will report activity stop to server.
         * @param keepShown Flag indicating whether the activity is still shown.
         * @param saveState Flag indicating whether the activity state should be saved.
         * @param finalStateRequest Flag indicating if this call is handling final lifecycle state
         *                          request for a transaction.
         * @param reason Reason for performing this operation.
         */
        private void performStopActivityInner(ActivityClientRecord r, StopInfo info, boolean keepShown,
                boolean saveState, boolean finalStateRequest, String reason) {
            if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
            if (r != null) {
                if (!keepShown && r.stopped) {
                    if (r.activity.mFinished) {
                        // If we are finishing, we won't call onResume() in certain
                        // cases.  So here we likewise don't want to call onStop()
                        // if the activity isn't resumed.
                        return;
                    }
                    if (!finalStateRequest) {
                        final RuntimeException e = new RuntimeException(
                                "Performing stop of activity that is already stopped: "
                                        + r.intent.getComponent().toShortString());
                        Slog.e(TAG, e.getMessage(), e);
                        Slog.e(TAG, r.getStateString());
                    }
                }
    
                // One must first be paused before stopped...
                performPauseActivityIfNeeded(r, reason);
    
                if (info != null) {
                    try {
                        // First create a thumbnail for the activity...
                        // For now, don't create the thumbnail here; we are
                        // doing that by doing a screen snapshot.
                        info.setDescription(r.activity.onCreateDescription());
                    } catch (Exception e) {
                        if (!mInstrumentation.onException(r.activity, e)) {
                            throw new RuntimeException(
                                    "Unable to save state of activity "
                                    + r.intent.getComponent().toShortString()
                                    + ": " + e.toString(), e);
                        }
                    }
                }
    
                if (!keepShown) {
                    callActivityOnStop(r, saveState, reason);
                }
            }
        }
        ...
        ...
        /**
         * Calls {@link Activity#onStop()} and {@link Activity#onSaveInstanceState(Bundle)}, and updates
         * the client record's state.
         * All calls to stop an activity must be done through this method to make sure that
         * {@link Activity#onSaveInstanceState(Bundle)} is also executed in the same call.
         */
        private void callActivityOnStop(ActivityClientRecord r, boolean saveState, String reason) {
            // Before P onSaveInstanceState was called before onStop, starting with P it's
            // called after. Before Honeycomb state was always saved before onPause.
            //                saveState r.state == null saveState   performStopActivityInner(r, stopInfo, show, true /* saveState */, finalStateRequest,    true
                    reason);
            final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                    && !r.isPreHoneycomb();
            // targetSdkVersion < android P
            final boolean isPreP = r.isPreP();
            if (shouldSaveState && isPreP) {
                callActivityOnSaveInstanceState(r);
            }
    
            try {
                r.activity.performStop(false /*preserveWindow*/, reason);
            } catch (SuperNotCalledException e) {
                throw e;
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to stop activity "
                                    + r.intent.getComponent().toShortString()
                                    + ": " + e.toString(), e);
                }
            }
            r.setState(ON_STOP);
            // targetSdkVersion >= android P
            if (shouldSaveState && !isPreP) {
                callActivityOnSaveInstanceState(r);
            }
        }
    

    handleStopActivity->performStopActivity Inner->callActivity OnStopを使用して、関連する呼び出しcallActivity OnSaveInstanceStateを見つけ、onSaveInstanceStateメソッド呼び出しに入ります.
    最後に
    正しくない点があればご指摘ください