本文基于 Android8.0 源码
0. 相关分享
Android源码分析 - Parcel 与 Parcelable
Android源码分析 —— Activity栈管理(基于Android8)
Android从屏幕刷新到View的绘制(一)之 Window、WindowManager和WindowManagerService之间的关系
1. 何时会调用 onSaveInstanceState?
onSaveInstanceState(Bundle savedInstance) 是 Android 用来保存一些异常情况下(例如由横竖屏切换等)而导致 Activity 销毁的数据。通过这个方式来保持 Activity 状态的方式,可以在下次再次打开此 Activity 时恢复数据。当然,如果用户主动返回,例如 finish() 掉 Activity,或者按了 Home键,这是不会走 onSaveInstanceState() 方法的。
我们从源码的角度来看一下是在哪里发起 onSaveInstance() 的,来到 ActivityThread.java,AMS(ActivityManagerService)通过ApplicationThread给APP进程发送通知,调度Activity,关于Activity销毁,可能会走 onPause(),onStop(),onDestroy()。在performPauseActivity()和performStopActivity()中,在异常退出的情况下,会回调 onSaveInstanceState()
进行状态保存.
1.1 handlePauseActivity() -> performPauseActivity()
//ActivityThread.java
final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,boolean saveState, String reason) {
//...
//如果finished为true,说明是用户主动退出的Activity,例如finish()方法,或者按了Home键。
if (finished) {
r.activity.mFinished = true;
}
if (!r.activity.mFinished && saveState) {
//如果不是用户主动发起关闭的,而且需要保存状态,就调用保存方法
callCallActivityOnSaveInstanceState(r);
}
//回调Activity的onPause()方法
performPauseActivityIfNeeded(r, reason);
//...
return !r.activity.mFinished && saveState ? r.state : null;
}
private void performPauseActivityIfNeeded(ActivityClientRecord r, String reason) {
if (r.paused) {
//如果已经pause了,不用再回调onPause()了
return;
}
try {
r.activity.mCalled = false;
//通过Instrumentation回调Activity组件的onPause()方法
mInstrumentation.callActivityOnPause(r.activity);
} catch (Exception e) {
throw e;
}
r.paused = true;
}
可以看到,当 ActivityThread.java 收到对某个 Activity 的onPause()请求时,首先会判断 finished 变量,它表示了 Activity 是用户主动退出的,还是尤其其他情况导致的onPaused。如果是因为后者,也就是异常因素,就会进入到 callCallActivityOnSaveInstanceState(r)
方法保存状态,然后再进入到 Activity 的 onPause() 回调。
关注到 callCallActivityOnSaveInstanceState() 的内容:
//ActivityThread.java
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
//将数据存到 Bundle 中
r.state = new Bundle();
r.state.setAllowFds(false);
//这个 persistable 是在配置文件中可以设置的属性,这个是持久化存储的能力,可以实现系统关机后重启的数据恢复能力。而普通的 savedInstanceState 只是存在了系统的内存中。
if (r.isPersistable()) {
r.persistentState = new PersistableBundle();
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
}
}
其中,persistable是Android 5.0 提供的新的配置,在Manifest配置文件中,可以为 Activity 设置一个属性:
android:persistableMode="persistAcrossReboots|persistRootOnly|persistNever"
- persistNever:总不起作用
- persistRootOnly:默认值,当ActivityRecord是TaskRecord的最底层时生效。
- persistAcrossReboots:标志了的Activity都会进行持久化状态数据保存。如果所在TaskRecord之上的ActivityRecord也设置了这个值,同时也会被持久化。
如果设置了这个值,在保存状态信息的时候除了保存在 r.state 这个 Bundle对象里,还会保存在 r.persistentState 这个 PersistableBundle对象中。简单来看一下是如何持久化存储的,显然要通过文件存储:
//PersistableBundle
public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
unparcel();
XmlUtils.writeMapXml(mMap, out, this);
}
public static PersistableBundle restoreFromXml(XmlPullParser in) throws IOException,
XmlPullParserException {
final int outerDepth = in.getDepth();
final String startTag = in.getName();
final String[] tagName = new String[1];
int event;
while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
return new PersistableBundle((ArrayMap<String, Object>)
XmlUtils.readThisArrayMapXml(in, startTag, tagName,
new MyReadMapCallback()));
}
}
return EMPTY;
}
@Override
public void writeUnknownObject(Object v, String name, XmlSerializer out)
throws XmlPullParserException, IOException {
if (v instanceof PersistableBundle) {
out.startTag(null, TAG_PERSISTABLEMAP);
out.attribute(null, "name", name);
((PersistableBundle) v).saveToXml(out);
out.endTag(null, TAG_PERSISTABLEMAP);
} else {
throw new XmlPullParserException("Unknown Object o=" + v);
}
}
查看到其源码,发现是通过xml文件来进行的数据持久化存储。它相比Bundle额外实现了 XmlUtils.WriteMapCallback 接口。
1.2 handleStopActivity() -> performStopActivityInner()
三个结束Activity的生命周期中,都有保存状态的尝试,onStop也不例外。
//ActivityThread.java
private void performStopActivityInner(ActivityClientRecord r,StopInfo info, boolean keepShown, boolean saveState, String reason) {
//...
if (r != null) {
//如果stop的时候发现activity连pause都还没有,会先进行pause,然后继续stop任务
performPauseActivityIfNeeded(r, reason);
//尝试保存状态信息
if (!r.activity.mFinished && saveState) {
if (r.state == null) {
//如果不是用户主动退出的Activity,且之前没有保存过状态信息,这里会调用 onSaveInstanceState()进行保存。
callCallActivityOnSaveInstanceState(r);
}
}
//如果仍然可见,但是位于下方,则不会调用onStop
if (!keepShown) {
try {
//回调onStop()方法
r.activity.performStop(false /*preserveWindow*/);
} catch (Exception e) {
//...
}
r.stopped = true;
}
}
}
这里和onPause生命周期类似,都会尝试对状态信息进行保存。不同的是,如果之前已经保存过状态信息了(performPauseActivity()中已经保存过了),这里就不会再次保存,浪费资源。
1.3 handleDestroyActivity() -> performDestroyActivity()
在Destroy()的回调过程中,并没有执行状态保存,只是健壮性判断,如果之前没有调用过pause或者stop,就会调用,而在pause和stop过程中,就已经有过状态保存了。
在 handleDestroyActivity() 中,首先调用了 performDestroyActivity(),之后就开始了Activity的关闭,与WMS通信,通知当前ActivityWindow的关闭。接下来也会通过 ActivityManager.getService().activityDestroyed(token)
通知 AMS,同时将状态数据(ActivityClientRecord的数据)回传给AMS(中的ActivityRecord)。
//ActivityThread.java
private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
if (finishing) {
r.activity.mFinished = true;
}
//如果还没pause,先pause
performPauseActivityIfNeeded(r, "destroy");
//如果还没stop,先stop
r.activity.performStop(r.mPreserveWindow);
//最后回调onDestory
mInstrumentation.callActivityOnDestroy(r.activity);
}
也就是说,只要Activity要进行关闭,不论到stop还是Destroy,都会先尝试将pause的情况走完,也就是总会调用到 performPauseActivity(),而其中也总会尝试将state状态数据进行保存。
2. onSaveInstanceState 存了什么内容?
上文可以看到,最后通过Instrumentation发起了Activity的 onSaveInstanceState() , 它最终会保存:
- Window下,有id且需要保存数据的View的数据信息
- Fragment数据信息
- Dialog数据信息
- 其他数据信息
我们首先看到调用入口 callCallActivityOnSaveInstanceState()
:
//ActivityThread.java
private void callCallActivityOnSaveInstanceState(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);
}
}
//Instrumentation.java
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
activity.performSaveInstanceState(outState);
}
接下来就来到了Activity的 performSaveInstanceState()
//Activity.java
final void performSaveInstanceState(Bundle outState) {
//保存窗口信息、以及Fragment信息
onSaveInstanceState(outState);
//通知所有Dialog进行状态保存
saveManagedDialogs(outState);
mActivityTransitionState.saveState(outState);
storeHasCurrentPermissionRequest(outState);
}
protected void onSaveInstanceState(Bundle outState) {
//保存窗口信息(保存View)
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
//保存所有Fragmet信息
Parcelable p = mFragments.saveAllState();
if (p != null) {
outState.putParcelable(FRAGMENTS_TAG, p);
}
//其他...
if (mAutoFillResetNeeded) {
outState.putBoolean(AUTOFILL_RESET_NEEDED, true);
getAutofillManager().onSaveInstanceState(outState);
}
getApplication().dispatchActivitySaveInstanceState(this, outState);
}
我们主要关注保存了哪些数据,首先看到 mWindow.saveHierarchyState()
,Activity的Window只有PhoneWindow实现,我们直接看到PhoneWindow:
//PhoneWindow.java
//这个是window content,要么是DecorView(没设置setContentView(),默认DecorView),要么是DecorView的孩子(setContentView())
ViewGroup mContentParent;
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
//SparseArray来临时保存信息,这是一种Key为integer类型,Value为任意类型的数据结构,是Google为Android设计的内存优化的数据结构。
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
//来到了ViewGroup的保存,显然这里只会保存DecorView,或者setContentView设置的视图
mContentParent.saveHierarchyState(states);
//包存到outState中
outState.putSparseParcelableArray(VIEWS_TAG, states);
//还需要保存获得焦点的是哪个View,保存它的id
final View focusedView = mContentParent.findFocus();
if (focusedView != null && focusedView.getId() != View.NO_ID) {
outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
}
// save the panels
SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
savePanelState(panelStates);
if (panelStates.size() > 0) {
outState.putSparseParcelableArray(PANELS_TAG, panelStates);
}
//ToolBar的信息也要保存
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
PhoneWindow会保存View的数据、Panel的数据、ToolBar的数据。我们关注到View数据的保存。它首先会通过 ViewGroup,这个要么是 DecorView,要么是用户通过 setContentView()
设置的一个 ViewGroup,调用其 mContentParent.saveHierarchyState()
,这在 View.java 中实现:
//View.java
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
//只要View有id,并且没有取消状态保存的标志,就可以被保存
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
//回调View的onSaveInstanceState()
Parcelable state = onSaveInstanceState();
if (state != null) {
//将结果放到container中
container.put(mID, state);
}
}
}
对于View本身而言,就是通过onSaveInstanceState()保存自身的状态,但是只看View.java本身的代码并没有保存什么信息,通常View级别的子类会将保存逻辑写在重写的onSaveInstanceState()
中。我们关注到它的实现类,例如TextView:
//TextView.java
@Override
public Parcelable onSaveInstanceState() {
//先调用父类View的onSaveInstanceState()方法,拿到一个Parcelable
Parcelable superState = super.onSaveInstanceState();
//这个boolean来自配置文件的 freezesText 标签设置
final boolean freezesText = getFreezesText();
boolean hasSelection = false;
int start = -1;
int end = -1;
//CharSequence类型的mText
if (mText != null) {
//如果有选择的起始点、终止点,说明之前有光标,记录光标和选中状态
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
hasSelection = true;
}
}
//如果设置了freezesText标志,或者有选中状态,就进行文字的状态保存
if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
if (freezesText) {
//如果是富文本的内容,按富文本的方式进行保存
if (mText instanceof Spanned) {
final Spannable sp = new SpannableStringBuilder(mText);
if (mEditor != null) {
removeMisspelledSpans(sp);
sp.removeSpan(mEditor.mSuggestionRangeSpan);
}
ss.text = sp;
} else {
//如果是普通文本,直接存为String类型
ss.text = mText.toString();
}
}
//保存光标信息
if (hasSelection) {
ss.selStart = start;
ss.selEnd = end;
}
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
ss.error = getError();
if (mEditor != null) {
//如果有编辑器,同时也记录编辑器的状态信息
ss.editorState = mEditor.saveInstanceState();
}
return ss;
}
return superState;
}
TextView的状态存储,首先调用了父类View的默认onSaveInstanceState()实现,然后将自己的数据填入其中,包括文本信息、光标选中状态、编辑器Editor。
View的子类ViewGroup则是重写了dispatchSaveInstanceState()
方法,就是收集其下所有子View的状态,进行综合保存。
@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);
}
}
}
3. 保存的数据何去何从?
显然保存的数据存在Bundle中的,Bundle实现了Parcelable接口,我们能猜到最终这个数据会包存到某个内存中。继续猜测,APP进程可能会被杀,而且Activity的发起实际是通过AMS,我们也能够猜到这个数据是存在AMS所在进程的内存空间中。后续代码跟踪也证实了这一点:
- 保存的数据存在AMS所在进程(SystemServer)的内存空间中(写在ActivityRecord中)。
- 启动Activity的时候,可以通过binder通信,将保存的数据传递回去。
我们回顾到最初发起 onSaveInstanceState 的地方:
//ActivityThread.java
private void callCallActivityOnSaveInstanceState(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);
}
}
显然,我们最终将所有状态信息写到了这个 ActivityClientRecord 对象中。最后通过AMS上报生命周期状态(同时也上报了状态信息)
//ActivityThread.java
private void handlePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges, boolean dontReport, int seq) {
//...
ActivityClientRecord r = mActivities.get(token);
//...
performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
//...
ActivityManager.getService().activityPaused(token);
}
可以看到,最后将存储后的内容交给了AMS,所有的信息保存在了AMS的ActivityRecord之中。假设Activity所在的进程被杀了,或者旋转屏幕了,再次启动这个Activity的时候,AMS复用之前的ActivityRecord,并从其中得到之前保存的数据。换句话说,存的这些状态数据,保存在了SystemServer进程中。
留存疑问,如果Activity的启动模式是SingleTask,是否会复用之前的ActivityRecord?如果是,那么保存的状态信息是否丢失了?
4. 保存的数据通过 onRestoreInstanceState() 进行恢复
Activity的 onRestoreInstanceState()
方法,在ActivityThread.java中,通过 performLaunchActivity() 方法进行回调:
//ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//1.activity.attach主要是创建Window
activity.attach(...);
//2.activity.onCreat(),分为是否persistable
if(r.isPersistable()){
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
}else{
mInstrumentation.callActivityOnCreate(activity, r.state);
}
//onStart()方法
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
//r.state!=null的话,说明之前异常退出,会回调onRestoreInstanceState()
if(!r.activity.mFinished){
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);
}
}
}
如果这个Activity是第一次进入,之前并没有异常退出,那么r.state就是空的,调用到 onCreate() 时传入的 Bundle savedInstanceState 也就是空的。同样的,如果之前没有异常退出,r.persistentState也是空的。
如果之前异常退出了(或者屏幕旋转了),那么r.state就非空,在onCreate()中可以获取到其内容,接下来也将回调 onRestoreInstanceState()
。
onRestoreInstanceState() 默认实现是恢复window的信息:
//Activity.java
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
//恢复window
mWindow.restoreHierarchyState(windowState);
}
}
}
PhoneWindow.restoreHierarchyState()
也是通过DecorView或者ViewGroup来进行视图数据恢复:
//PhoneWindow.java
@Override
public void restoreHierarchyState(Bundle savedInstanceState) {
if (mContentParent == null) {
return;
}
SparseArray<Parcelable> savedStates
= savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
if (savedStates != null) {
//View数据恢复
mContentParent.restoreHierarchyState(savedStates);
}
//焦点View恢复
int focusedViewId = savedInstanceState.getInt(FOCUSED_ID_TAG, View.NO_ID);
if (focusedViewId != View.NO_ID) {
View needsFocus = mContentParent.findViewById(focusedViewId);
if (needsFocus != null) {
needsFocus.requestFocus();
}
}
//Panels恢复
SparseArray<Parcelable> panelStates = savedInstanceState.getSparseParcelableArray(PANELS_TAG);
if (panelStates != null) {
restorePanelState(panelStates);
}
//Toolbar数据恢复
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates =
savedInstanceState.getSparseParcelableArray(ACTION_BAR_TAG);
if (actionBarStates != null) {
doPendingInvalidatePanelMenu();
mDecorContentParent.restoreToolbarHierarchyState(actionBarStates);
}
}
}
5. 保存额外信息
通过重写 onSaveInstanceState()
方法,往bundle中存入额外数据,需要注意的是,这个bundle最后也是要通过binder通信发送到AMS的,所以数据量不能太多:
//MyActivity.java
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState){
//调用父类的方法(父类默认实现了View视图的状态保存)
super.onSaveInstanceState(outState,outPersistentState);
outStte.putString("test",str);
}
可以在 onRestoreInstanceState()
中进行恢复:
@Override
public void onRestoreInstanceState(Bundle savedInstanceState,PersistableBundle persistentState){
//调用父类的方法(父类默认实现了View状态的恢复)
super.onRestoreInstanceState(savedInstanceState,persistentState);
str = savedInstanceState.getString("temp");
}
也可以在 onCreate()
中进行恢复(检查Bundle是否为空):
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstance != null){
//之前有状态数据信息保存,可能还有自己额外保存的数据
str = savedInstanceState.getString("temp");
}
}
6. 实验:
MainActivity如下
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Fy_MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG,"onCreate");
}
//所有生命周期都打印
//...
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.e(TAG,"onSaveInstanceState");
outState.putString("test","im Tu Fengyi");
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String s = savedInstanceState.getString("test");
Log.e(TAG,"onRestoreInstanceState gets s:"+s);
}
}
旋转屏幕后,打印结果为:(Android8.0):
onCreate
onStart
onResume
onPause
onSaveInstanceState
onStop
onDestroy
onCreate
onStart
onRestoreinstanceState gets s:im Tu Fengyi
onResume
旋转屏幕后,打印结果为:(Android9.0 之后改了onSaveInstance()回调的位置位置)
2023-03-14 20:04:08.859 12301-12301/? E/Fy_MainActivity: onCreate
2023-03-14 20:04:08.874 12301-12301/? E/Fy_MainActivity: onStart
2023-03-14 20:04:08.898 12301-12301/? E/Fy_MainActivity: onResume
2023-03-14 20:06:14.853 12301-12301/? E/Fy_MainActivity: onPause
2023-03-14 20:06:14.855 12301-12301/? E/Fy_MainActivity: onStop
2023-03-14 20:06:14.858 12301-12301/? E/Fy_MainActivity: onSaveInstanceState
2023-03-14 20:06:14.859 12301-12301/? E/Fy_MainActivity: onDestroy
2023-03-14 20:06:14.922 12301-12301/? E/Fy_MainActivity: onCreate
2023-03-14 20:06:14.926 12301-12301/? E/Fy_MainActivity: onStart
2023-03-14 20:06:14.927 12301-12301/? E/Fy_MainActivity: onRestoreInstanceState gets s:im Tu Fengyi
2023-03-14 20:06:14.927 12301-12301/? E/Fy_MainActivity: onResume