Android 11源码分析:onSaveInstanceState到底做了什么?你知道的调用时机真的是正确的吗?

可能大部分的安卓开发者对于onSaveInstanceState和onRestoreInstanceState是有熟悉又陌生,熟悉吧是因为经常听到,陌生吧,是因为平时用的少,只知道是保存和恢复Activity状态的,其他的再多也不知道了,那本文将在Android 11的源码基础上来分析4个问题。

  1. onSaveInstanceState做了什么

  2. onSaveInstanceState什么时候执行

  3. onRestoreInstanceState做了什么

  4. onRestoreInstanceState什么时候执行

1.onSaveInstanceState做了什么

android.app.Activity

  protected void onSaveInstanceState(@NonNull Bundle outState) {
        1.保存当前UI状态
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());

        outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
        2.保存Fragments状态
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
        if (mAutoFillResetNeeded) {
            outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
            getAutofillManager().onSaveInstanceState(outState);
        }
        dispatchActivitySaveInstanceState(outState);
    }
复制代码

mWindow是Window类型,Window是抽象类,唯一实现类是PhoneWindow,在Activity启动的时候,会执行Activity的attach方法,内部进行赋值

android.app.Activity
 final void attach(...){
     ......
       mWindow = new PhoneWindow(this, window, activityConfigCallback);
       mWindow.setWindowControllerCallback(mWindowControllerCallback);
       mWindow.setCallback(this);
     ......
 }
复制代码

· 稍微提一嘴,留意一下mWindow.setCallback(this);这行代码,感兴趣的可以自己去探索一下,之后的谋篇文章里,我应该也会提到这一句。

1.1saveHierarchyState

om.android.internal.policy.PhoneWindow

    @Override
    public Bundle saveHierarchyState() {
        Bundle outState = new Bundle();
        if (mContentParent == null) {
            return outState;
        }
        使用SparseArray存储
        SparseArray<Parcelable> states = new SparseArray<Parcelable>();
        mContentParent其实就是setContentParent设置进来的View
        mContentParent.saveHierarchyState(states);
        将视图结构保存进outState
        outState.putSparseParcelableArray(VIEWS_TAG, states);

        // Save the focused view ID.
        根据注释理解为保存获取了焦点的View
        final View focusedView = mContentParent.findFocus();
        if (focusedView != null && focusedView.getId() != View.NO_ID) {
            //只保存设置了ID的View
            outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
        }

        // save the panels
        panels是面板的意思,这块保存的面板具体是什么我也不太清楚,有知道的小伙伴可以给我科普科普
        SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
        savePanelState(panelStates);
        if (panelStates.size() > 0) {
            outState.putSparseParcelableArray(PANELS_TAG, panelStates);
        }
        保存ActionBar状态
        if (mDecorContentParent != null) {
            SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
            mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
            outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
        }

        return outState;
    }
复制代码

重点关注一下mContentParent.saveHierarchyState(states); 方法

android.view.View

public void saveHierarchyState(SparseArray<Parcelable> container) {
    dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    ①
    if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
        mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
        ②
        Parcelable state = onSaveInstanceState();
       if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
            throw new IllegalStateException(
                    "Derived class did not call super.onSaveInstanceState()");
        }
        if (state != null) {
            // Log.i("View", "Freezing #" + Integer.toHexString(mID)
            // + ": " + state);
            ③
            container.put(mID, state);
        }
    }
}
复制代码

出,如果没有View没有Id,则不会保存状态,通过 onSaveInstanceState() 获取自身状态,如果自身的状态不为null,则在处保存状态。而onSaveInstanceState默认返回的空状态

@CallSuper
@Nullable protected Parcelable onSaveInstanceState() {
    ......
    return BaseSavedState.EMPTY_STATE;
}
复制代码

所以如果想保存View的状态你需要覆写onSaveInstanceState方法。

其实还有个会疑问,我们setContentView的必须是一个ViewGroup的类型啊,这个View的方法似乎满足不了我们的需求,所以我们去看看ViewGroup中是否复写了该方法。

android.view.ViewGroup

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    super.dispatchSaveInstanceState(container);
    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        View c = children[i];
        if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
            c.dispatchSaveInstanceState(container);
        }
    }
}
复制代码

果不其然,ViewGroup覆写了该方法。在第一行调用父类的dispatchSaveInstanceState方法,目的是保存自身的状态,其次循环自己的子View,执行子View的dispatchSaveInstanceState,如果子View任然是ViewGroup则再次执行改流程。和分析View的事件分发流程似乎有一丢丢相似。

onSaveInstanceState什么时候执行

网上大部分的文章对于onSaveInstanceState的描述基本上是打印几个生命周期的日志,然后告诉结论:是在onPause之后,在onStop之前调用。深入些的会在源码中找到依据,那到底是不是这样呢?

android.app.Activity
    /**
     * The hook for {@link ActivityThread} to save the state of this activity.
     *
     * Calls {@link #onSaveInstanceState(android.os.Bundle)}
     * and {@link #saveManagedDialogs(android.os.Bundle)}.
     *
     * @param outState The bundle to save the state to.
     * @param outPersistentState The bundle to save persistent state to.
     */
    final void performSaveInstanceState(@NonNull Bundle outState,
            @NonNull PersistableBundle outPersistentState) {
        dispatchActivityPreSaveInstanceState(outState);
        //保存状态
        onSaveInstanceState(outState, outPersistentState);
        saveManagedDialogs(outState);
        storeHasCurrentPermissionRequest(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
                ", " + outPersistentState);
        dispatchActivityPostSaveInstanceState(outState);
    }
复制代码

Activity调用onSaveInstanceState的方法是在上面代码中,根据方法的注释,懂的人就知道直接去ActivityThread中找调用的地方了,但是却搜不到?别着急,在之前的文章中多次提到Instrumentation会管理Activity的生命周期,所以performSaveInstanceState的调用其实是在Instrumentation中。

android.app.Instrumentation

    public void callActivityOnSaveInstanceState(@NonNull Activity activity,
            @NonNull Bundle outState) {
        activity.performSaveInstanceState(outState);
    }
复制代码

那这又是在什么地方调用的呢?看过之前文章的小伙伴,可能会有一种印象,Instrumentation*在ActivityThread中有个变量mInstrumentation,所以猜测大概率也是在那调用的。不过这次直接在通过AS点击方法能直接跳到调用的地方。确实是在ActivityThread

android.app.ActivityThread

    private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        关键代码
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }
复制代码

所以最后还是由ActivityThread去控制Instrumentation执行ActivityperformSaveInstanceState方法。那现在只需要找到:ActivityThread中的callActivityOnSaveInstanceState是什么时候执行的,就可以知道问题onSaveInstanceState什么时候调用的答案。

我是用AS看源码的,所以只需要双击callActivityOnSaveInstanceState方法,用快捷键Alt+F7就可以找到方法使用的地方了。

1628746703(1).png

由图可知调用了三次,分别在执行onPause和onStop的方法的时候,具体调用顺序还需要在细看。虽然这两个方法上层还有几次调用,但最后都是在TransactionExecutor类下的performLifecycleSequence方法中发起。对这快有兴趣的可以看看Android 11源码分析: Activity的启动流程

这这这……不是说在onPause之后,在onStop之前么。怎么还有三处调用呢?这可不是我下的结论,我的结论还没说呢,继续跟踪找答案。

先看第一次调用的地方

android.app.ActivityThread

   private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason,
            PendingTransactionActions pendingActions) {
            ......
            // Pre-Honeycomb apps always save their state before pausing
            final boolean shouldSaveState = !r.activity.mFinished && r.isPreHoneycomb();
            if (shouldSaveState) {
                关键代码
                callActivityOnSaveInstanceState(r);
            }
            内部通过Instrumentation,调用Acivity的performPause
            方法,进而执行onPause
            performPauseActivityIfNeeded(r, reason);
            ......
   }
复制代码
  1. performPauseActivity方法中,shouldSaveState为true,则会执行onSaveInstanceState

  2. r.activity.mFinished为false,且r.isPreHoneycomb() 为true的时候shouldSaveState为true

mFinished是Activity的全局变量,只有在执行生命周期finish的时候,才为true。isPreHoneycomb()的代码如下

android.app.ActivityThread

    private boolean isPreHoneycomb() {
            return activity != null && activity.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.HONEYCOMB;
        }
复制代码

HONEYCOMB常量11,也就是SDK11(Android 3.0)

结论1:当Activity没有finish生命周期,且当前版本在安卓3.0之前的,OnSaveInstanceState方法在onPause之前调用

现在基本上没有安卓3.0的设备了吧。

后面两次调用都在callActivityOnStop方法内。一个方法里,执行两次OnSaveInstanceState又是啥原因呢?

android.app.ActivityThread
    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.
        final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
        final boolean isPreP = r.isPreP();
        if (shouldSaveState && isPreP) {
            第二处
            callActivityOnSaveInstanceState(r);
        }

        try {
             执行onStop
            r.activity.performStop(r.mPreserveWindow, 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);

        if (shouldSaveState && !isPreP) {
            第三处
            callActivityOnSaveInstanceState(r);
        }
    }
复制代码

这块代码很有意思,第二处执行在onStop之前,第三处在onStop之后。且执行条件区别就在于这个isPreP。

android.app.ActivityThread

     private boolean isPreP() {
            return activity != null && activity.getApplicationInfo().targetSdkVersion
                    < android.os.Build.VERSION_CODES.P;
        }

复制代码

Android P 也就是SDK 28 安卓9

结论已经很明显了,再加上方法的前面两行给的注释

Before P onSaveInstanceState was called before onStop, starting with P it’s
called after. Before Honeycomb state was always saved before onPause.

结论2:安卓3.0之后至9.0之前,OnSaveInstanceState方法在onPause之后onStop之前调用这个也是目前网上绝大部分文章的结论。

结论3:安卓9.0之后OnSaveInstanceState方法在onStop之后调用

 final boolean shouldSaveState = saveState && !r.activity.mFinished && r.state == null
                && !r.isPreHoneycomb();
复制代码

再反过头来看shouldSaveState变量,他也很重要,如果他为false,则无论什么版本都不会执行OnSaveInstanceState方法。我们都知道,只有未经你许可Activity的时候,我们才会需要保存数据,并不是每次都会执行OnSaveInstanceState方法的。那可想而知,shouldSaveState就是什么叫未经你许可的体现了。

从后往前看:

  1. !isPreHoneycomb() : 大于安卓3.0

  2. r.state == null : state是ActivityClientRecord下的Bundle类型,也是后面onRestoreInstanceState需要恢复的数据。简而言之:没有保存过数据,则进行保存,保存过则不需要再次保存

  3. !r.activity.mFinished: 之前讲过

  4. saveState:这个变量是关键。设计到的case有点多,如果再次进入细节分析篇幅过程且同时我也还没想好怎么去分析。所以先列出一些实际情况为true的操作。 以下描述参考《Android开发艺术探索》

· 当用户按下HOME键时
· 长按HOME键,选择运行其他的程序时
· 按下电源按键(关闭屏幕显示)时 > · 从activity A中启动一个新的activity时
. 屏幕方向切换
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。

onRestoreInstanceState做了什么

相对于onSaveInstanceState来说onRestoreInstanceState就简单不少了,一个保存,那另一个就是恢复了。

android.app.Activity

public void onRestoreInstanceState(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    if (savedInstanceState != null) {
        onRestoreInstanceState(savedInstanceState);
    }
}
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
    if (mWindow != null) {
        Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
        if (windowState != null) {
            关键代码
            mWindow.restoreHierarchyState(windowState);
        }
    }
}
复制代码

不用说也知道,前面费劲心思保存的Bundle类型的outState,就是onRestoreInstanceState需要恢复的windowState。具体执行也是在PhonewWindow下的restoreHierarchyState中,其实就是把之前保存的操作,反向在恢复一次。感兴趣的同学可以自行去了解一下。

onRestoreInstanceState什么时候执行

onRestoreInstanceState的执行时机,其实没什么争议,就是在onStart之前,但是我们还是自己去探索一下吧,这样放心。

android.app.Activity
/**
 * The hook for {@link ActivityThread} to restore the state of this activity.
 *
 * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
 * {@link #restoreManagedDialogs(android.os.Bundle)}.
 *
 * @param savedInstanceState contains the saved state
 * @param persistentState contains the persistable saved state
 */
final void performRestoreInstanceState(@Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
    执行
    onRestoreInstanceState(savedInstanceState, persistentState);
    if (savedInstanceState != null) {
        restoreManagedDialogs(savedInstanceState);
    }
}
复制代码

可以看到,onRestoreInstanceState方法在performRestoreInstanceState中调用,根据熟悉的注释,我们这次学乖了,直接去Instrumentation类里找,肯定又是包装了一层在给ActivityThread用。

android.app.Instrumentation

public void callActivityOnRestoreInstanceState(@NonNull Activity activity,
        @Nullable Bundle savedInstanceState,
        @Nullable PersistableBundle persistentState) {
        调用activity的performRestoreInstanceState
    activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
复制代码

果不其然

@Override
public void handleStartActivity(IBinder token, PendingTransactionActions pendingActions) {
     ......
      ①
    // Start
    activity.performStart("handleStartActivity");
    if (pendingActions == null) {
    // No more work to do.
    return;
    }
    ②
    // Restore instance state
   
    if (pendingActions.shouldRestoreInstanceState()) {
        if (r.isPersistable()) {
            if (r.state != null || r.persistentState != null) {
                mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                        r.persistentState);
            }
        } else if (r.state != null) {
            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
        }
    }
    ......
}
复制代码

上面代码中,官方的两个简单的注释,已经很明白了。执行OnRestoreInstanceState方法必然是在Activity的onStart之后。

总结

  1. OnSaveInstanceState会保存当前页面的UI状态,用户可以将需要保存的数据也写进outState中
  2. 在安卓3.0版本以前,OnSaveInstanceState方法在onPause之前执行
  3. 在安卓3.0至安卓9.0版本中,如果是未经许可时销毁则OnSaveInstanceState方法在onPause之后,onStop之前执行
  4. 在安卓9.0之后,如果是未经许可时销毁OnSaveInstanceState方在onStop之后执行
  5. OnRestoreInstanceState会将进行视图恢复,同时提供用户之前保存的数据
  6. Activity重建后,OnRestoreInstanceState会在onStart之后执行
  7. OnSaveInstanceState和onRestoreInstanceState是设计模式中备忘录模式的体现
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享