1. onSaveInstanceState简介
如果系统由于系统约束(而不是正常的应用程序行为)而破坏了Activity,那么尽管实际 Activity实例已经消失,但是系统还是会记住它已经存在,这样如果用户导航回到它,系统会创建一个新的实例的Activity使用一组保存的数据来描述Activity在被销毁时的状态。系统用于恢复以前状态的已保存数据称为“实例状态”,是存储在Bundle对象中的键值对的集合。
1.1 onSaveInstanceState简介
a. onSaveInstanceState(Bundle outState)
保存Acticivity被销毁前的状态:在离开页面的时候用onSaveInstanceState中的outState可以保存你所需要的值
b. onRestoreInstanceState(Bundle savedInstanceState)
恢复Acticivity被销毁前的状态:在重新回到该页面的时候可以使用onRestoreInstanceState从saveInstanceState中获取保存过得值来重新初始化界面
onSaveInstanceState是在OnStop之前执行,onRestoreInstanceState在OnStart后执行
注意:onSaveInstanceState()是用来保存UI状态,onPause()适合用于数据的持久化保存
1.2 onSaveInstanceState被调用的情况
1.2.1 onSaveInstanceState什么时候会被调用
a. 当用户按下HOME键
b. 长按HOME键,选择运行其他的程序
c. 按下电源按键(关闭屏幕显示)
d. 从activity A中启动一个新的activity
e. 屏幕方向切换时,例如从竖屏切换到横屏
在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行
注意:当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态
1.2.2 onRestoreInstanceState什么时候会被调用
onRestoreInstanceState被调用的前提:Activity确实被系统销毁了,并不是和onSaveInstanceState成对出现
例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行
2. onSaveInstanceState使用方法
2.1 保存销毁前状态
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt("newsCenter_position", newsCenterPosition);
outState.putInt("smartService_position", smartServicePosition);
outState.putInt("govAffairs_position", govAffairsPosition);
super.onSaveInstanceState(outState);
}
2.2 恢复销毁前状态
a. 恢复销毁前保存的数据
@Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null && savedInstanceState.containsKey("newsCenter_position")) {
newsCenterPosition = savedInstanceState.getInt("newsCenter_position");
smartServicePosition = savedInstanceState.getInt("smartService_position");
govAffairsPosition = savedInstanceState.getInt("govAffairs_position");
}
super.onCreate(savedInstanceState);
}
b. 根据保存的数据恢复销毁前的状态
public void switchMenu(int type) {
switch (type) {
case NEWS_CENTER:
if (newsCenterAdapter == null) {
newsCenterAdapter = new MenuAdapter(ct, newsCenterMenu);
newsCenterclassifyLv.setAdapter(newsCenterAdapter);
} else {
newsCenterAdapter.notifyDataSetChanged();
}
newsCenterAdapter.setSelectedPosition(newsCenterPosition);
break;
case SMART_SERVICE:
if (smartServiceAdapter == null) {
smartServiceAdapter = new MenuAdapter(ct, smartServiceMenu);
smartServiceclassifyLv.setAdapter(smartServiceAdapter);
} else {
smartServiceAdapter.notifyDataSetChanged();
}
smartServiceAdapter.setSelectedPosition(smartServicePosition);
break;
case GOV_AFFAIRS:
if (govAffairsAdapter == null) {
govAffairsAdapter = new MenuAdapter(ct, govAffairsMenu);
govAffairsclassifyLv.setAdapter(govAffairsAdapter);
} else {
govAffairsAdapter.notifyDataSetChanged();
}
govAffairsAdapter.setSelectedPosition(govAffairsPosition);
break;
}
}
3. onSaveInstanceState源码分析
3.1 onSaveInstanceState源码分析
MainActivity.java:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("octopus", "www.baidu.com");
}
Activity.java (frameworks\base\core\java\android\app) 321500 2017/8/25
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
protected void onSaveInstanceState(Bundle outState) {
outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId);
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);
}
}
PhoneWindow.java (frameworks\base\core\java\com\android\internal\policy) 143971 2017/8/25
public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public Bundle saveHierarchyState() {
Bundle outState = new Bundle();
if (mContentParent == null) {
return outState;
}
SparseArray<Parcelable> states = new SparseArray<Parcelable>();
mContentParent.saveHierarchyState(states);
outState.putSparseParcelableArray(VIEWS_TAG, states);
// Save the focused 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);
}
if (mDecorContentParent != null) {
SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
}
return outState;
}
}
View.java (frameworks\base\core\java\android\view) 1045455 2017/8/25
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public void saveHierarchyState(SparseArray<Parcelable> container) {
dispatchSaveInstanceState(container);
}
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) { //mID用来指定当前view的id,如果view没有指定id则默认为NO_ID
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState(); //保存view当前的状态到state
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的状态state以键值对的形式和mID绑定,并保存到SparseArray中
}
}
}
}
onSaveInstanceState分析结论:
view会调用onSaveInstanceState保存当前状态state,并将view的状态state以键值对的形式和mID绑定,并保存到SparseArray中
3.2 onRestoreInstanceState源码分析
View.java (frameworks\base\core\java\android\view) 1045455 2017/8/25
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
public void restoreHierarchyState(SparseArray<Parcelable> container) {
dispatchRestoreInstanceState(container);
}
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID) {
Parcelable state = container.get(mID); //根据mID从SparseArray中取出当前view的状态state
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
onRestoreInstanceState(state); //调用View.onRestoreInstanceState恢复当前view的状态
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}
@CallSuper
protected void onRestoreInstanceState(Parcelable state) {
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
if (state != null && !(state instanceof AbsSavedState)) {
throw new IllegalArgumentException("Wrong state class, expecting View State but "
+ "received " + state.getClass().toString() + " instead. This usually happens "
+ "when two views of different type have the same id in the same hierarchy. "
+ "This view's id is " + ViewDebug.resolveId(mContext, getId()) + ". Make sure "
+ "other views do not use the same id.");
}
if (state != null && state instanceof BaseSavedState) {
BaseSavedState baseState = (BaseSavedState) state;
if ((baseState.mSavedData & BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED) != 0) {
mStartActivityRequestWho = baseState.mStartActivityRequestWhoSaved;
}
if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) {
setAutofilled(baseState.mIsAutofilled);
}
if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) {
mAutofillViewId = baseState.mAutofillViewId;
}
}
}
}
onRestoreInstanceState分析结论:
view根据mID从SparseArray中取出当前view的状态state,并调用View.onRestoreInstanceState恢复当前view的状态
4. 用法总结
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null)
System.out.println("onCreate() : " + savedInstanceState.getString("octopus"));
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
System.out.println("onRestoreInstanceState() : " + savedInstanceState.getString("octopus"));
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("octopus", "www.baidu.com");
System.out.println("onSaveInstanceState() : save date www.baidu.com");
}
}
横竖屏切换,打印结果如下:
I/System.out( 8167): onSaveInstanceState() : save date www.baidu.com
I/System.out( 8167): onCreate() : www.baidu.com
I/System.out( 8167): onRestoreInstanceState() : www.baidu.com