前段时间面试被问到这么一个问题:
onSaveInstanceState的调用时机?
答:我说在按home键、锁屏(进入后台),配置文件发送改变的时候会去执行。
问:他说,你确定?不是在内存不足的时候才回调吗?按Home键真的会执行吗?那如果回调了onSaveInstanceState,那么进入前台的时候我们是不是每次都要从回调onRestoreIntanceSate中取值?连续问了强调了几次你确定。问的我都懵逼了,都不相信自己了。
Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(Bundle) or onRestoreInstanceState(Bundle) (the Bundle populated by this method will be passed to both). This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(Bundle) or onRestoreInstanceState(Bundle).
在activity被杀掉之前调用保存每个实例的状态,以保证该状态可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (传入的Bundle参数是由onSaveInstanceState封装好的)中恢复。这个方法在一个activity被杀死前调用,当该activity在将来某个时刻回来时可以恢复其先前状态。例如,如果activity B启用后位于activity A的前端,在某个时刻activity A因为系统回收资源的问题要被杀掉,A通过onSaveInstanceState将有机会保存其用户界面状态,使得将来用户返回到activity A时能通过onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢复界面的状态。
Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
不要将这个方法和activity生命周期回调如onPause()或onStop()搞混淆了,onPause()在activtiy被放置到背景或者自行销毁时总会被调用,onStop()在activity被销毁时被调用。一个会调用onPause()和onStop(),但不触发onSaveInstanceState的例子是当用户从activity B返回到activity A时:没有必要调用B的onSaveInstanceState(Bundle),此时的B实例永远不会被恢复,因此系统会避免调用它。一个调用onPause()但不调用onSaveInstanceState的例子是当activity B启动并处在activity A的前端:如果在B的整个生命周期里A的用户界面状态都没有被破坏的话,系统是不会调用activity A的onSaveInstanceState(Bundle)的。
The default implementation takes care of most of the UI per-instance state for you by calling onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself. If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause().
默认的实现负责了大部分UI实例状态(的保存),采用的方式是调用UI层上每个拥有id的view的onSaveInstanceState() ,并且保存当前获得焦点的view的id(所有保存的状态信息都会在默认的onRestoreInstanceState(Bundle)实现中恢复)。如果你覆写这个方法来保存额外的没有被各个view保存的信息,你可能想要在默认实现过程中调用或者自己保存每个视图的所有状态。如果被调用,这个方法会在onStop()前被触发,但系统并不保证是否在onPause()之前或者之后触发。
巴拉巴拉说了这么多,无非就是在讲一个事实,那就是onSaveInstanceState这个方法,是当系统“容易”杀死当前应用时会回调。上面第一段翻译有这么一句:“这个方法在一个activity被杀死前调用”,不要被这个字面意思骗了,难道真的是在起快要被杀死的时候才调用吗?肯定不是这样的。
再来看下Application Fundamentals上的一段话:
从这句话可以知道,当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
注意上面的双引号,何为“容易”?言下之意就是该activity还没有被销毁,而仅仅是一种可能性。这种可能性有哪些?通过重写一个activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我们可以清楚地知道当某个activity(假定为activity A)显示在当前task的最上层时,其onSaveInstanceState方法会在什么时候被执行,有这么几种情况:
1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
2、长按HOME键,选择运行其他的程序时(其实是进入到其他应用)。
3、按下电源按键(关闭屏幕显示)时。
4、从activity A中启动一个新的activity时。
5、屏幕方向切换时,例如从竖屏切换到横屏时。
在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
其实,最重要的就是记住,不是自己主动销毁的activity,必然会由系统来回调这个方法。从源码中可以看出端倪。
this.performPauseActivity(token, finished, r.isPreHoneycomb(), "handlePauseActivity");
final Bundle performPauseActivity(IBinder token, boolean finished, boolean saveState, String reason) {
ActivityThread.ActivityClientRecord r = (ActivityThread.ActivityClientRecord)this.mActivities.get(token);
return r != null?this.performPauseActivity(r, finished, saveState, reason):null;
}
public boolean isPreHoneycomb() {
return this.activity != null?this.activity.getApplicationInfo().targetSdkVersion < 11:false;
}
final Bundle performPauseActivity(ActivityThread.ActivityClientRecord r, boolean finished, boolean saveState, String reason) {
if(r.paused) {
if(r.activity.mFinished) {
return null;
}
RuntimeException listeners = new RuntimeException("Performing pause of activity that is not resumed: " + r.intent.getComponent().toShortString());
Slog.e("ActivityThread", listeners.getMessage(), listeners);
}
if(finished) {
r.activity.mFinished = true;
}
if(!r.activity.mFinished && saveState) {
this.callCallActivityOnSaveInstanceState(r);
}
this.performPauseActivityIfNeeded(r, reason);
ArrayMap size = this.mOnPauseListeners;
ArrayList var9;
synchronized(this.mOnPauseListeners) {
var9 = (ArrayList)this.mOnPauseListeners.remove(r.activity);
}
int var10 = var9 != null?var9.size():0;
for(int i = 0; i < var10; ++i) {
((OnActivityPausedListener)var9.get(i)).onPaused(r.activity);
}
return !r.activity.mFinished && saveState?r.state:null;
}
if(!r.activity.mFinished && saveState) { this.callCallActivityOnSaveInstanceState(r); }
07-11 16:00:59.106 21412-21412/com.txt.mydemo I/LifecycleActivity: onPause
07-11 16:00:59.146 21412-21412/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged
07-11 16:00:59.263 21412-21412/com.txt.mydemo I/LifecycleActivity: onSaveInstanceState
this.performPauseActivityIfNeeded(r, reason);
private void handleStopActivity(IBinder token, boolean show, int configChanges, int seq) {
ActivityThread.ActivityClientRecord r = (ActivityThread.ActivityClientRecord)this.mActivities.get(token);
if(checkAndUpdateLifecycleSeq(seq, r, "stopActivity")) {
r.activity.mConfigChangeFlags |= configChanges;
ActivityThread.StopInfo info = new ActivityThread.StopInfo(null);
this.performStopActivityInner(r, info, show, true, "handleStopActivity");
this.updateVisibility(r, show);
if(!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
info.activity = r;
info.state = r.state;
info.persistentState = r.persistentState;
this.mH.post(info);
this.mSomeActivitiesChanged = true;
}
}
private void performStopActivityInner(ActivityThread.ActivityClientRecord r, ActivityThread.StopInfo info, boolean keepShown, boolean saveState, String reason) {
if(r != null) {
if(!keepShown && r.stopped) {
if(r.activity.mFinished) {
return;
}
RuntimeException e = new RuntimeException("Performing stop of activity that is already stopped: " + r.intent.getComponent().toShortString());
Slog.e("ActivityThread", e.getMessage(), e);
Slog.e("ActivityThread", r.getStateString());
}
this.performPauseActivityIfNeeded(r, reason);
if(info != null) {
try {
info.description = r.activity.onCreateDescription();
} catch (Exception var8) {
if(!this.mInstrumentation.onException(r.activity, var8)) {
throw new RuntimeException("Unable to save state of activity " + r.intent.getComponent().toShortString() + ": " + var8.toString(), var8);
}
}
}
if(!r.activity.mFinished && saveState && r.state == null) {
this.callCallActivityOnSaveInstanceState(r);
}
if(!keepShown) {
try {
r.activity.performStop(false);
} catch (Exception var7) {
if(!this.mInstrumentation.onException(r.activity, var7)) {
throw new RuntimeException("Unable to stop activity " + r.intent.getComponent().toShortString() + ": " + var7.toString(), var7);
}
}
r.stopped = true;
EventLog.writeEvent(30049, new Object[]{Integer.valueOf(UserHandle.myUserId()), r.activity.getComponentName().getClassName(), reason});
}
}
}
if(!r.activity.mFinished && saveState && r.state == null) { this.callCallActivityOnSaveInstanceState(r); }
07-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onCreate sdk version: 25
07-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onStart
07-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onResume
07-11 16:29:49.006 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged
07-11 16:29:52.326 11766-11766/com.txt.mydemo I/LifecycleActivity: onPause
07-11 16:29:52.389 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged
07-11 16:29:52.584 11766-11767/com.txt.mydemo E/dalvikvm: GC_CONCURRENT freed 8160K, 30% free 42805K/60679K, paused 20ms+2ms, total 48ms
07-11 16:29:52.943 11766-11766/com.txt.mydemo I/LifecycleActivity: onSaveInstanceState
07-11 16:29:52.943 11766-11766/com.txt.mydemo I/LifecycleActivity: onStop
07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onRestart
07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onStart
07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onResume
07-11 16:29:56.334 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged