从源码角度看onSaveInstanceState和onRestoreInstanceState的调用时机

来源:互联网 发布:少儿英语软件推荐 编辑:程序博客网 时间:2024/05/22 01:47

引言:

前段时间面试被问到这么一个问题:

onSaveInstanceState的调用时机?

答:我说在按home键、锁屏(进入后台),配置文件发送改变的时候会去执行。

问:他说,你确定?不是在内存不足的时候才回调吗?按Home键真的会执行吗?那如果回调了onSaveInstanceState,那么进入前台的时候我们是不是每次都要从回调onRestoreIntanceSate中取值?连续问了强调了几次你确定。问的我都懵逼了,都不相信自己了。


onSaveInstanceState调用时机

先来看一段api的翻译:

protected void onSaveInstanceState(Bundle outState)

       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上的一段话:

 
 Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key)

从这句话可以知道,当某个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,必然会由系统来回调这个方法。从源码中可以看出端倪。


源码分析

在源码中,有两个地方会回调onSaveInstanceState方法,入口都是在ActivityThread中:
1、执行handlePauseActivity,该方法里面调用
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;    }
其中第三个参数影响着是否会去回调onSaveInstanceState方法,先看一下isPreHoneycomb()方法的执行结果:
public boolean isPreHoneycomb() {            return this.activity != null?this.activity.getApplicationInfo().targetSdkVersion < 11:false;        }
该方法是ActivityThread内部类ActivityClientRecord的方法,在该方法里面先判断了activity是否为空,很显然不为空,那么就会去执行targetSdkVersion<11的比较结果,也就是说,app当前的targetSdkVersion小于3.0的时候,才会返回true,其他时候就返回false,那么可以想象,大部分机子(android目前的机子基本上都是3.0以上了吧),在onPause的时候,应该都不会去执行执行onSaveInstanceState的回调了。为什么?继续看performPauseActivity方法:
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);}
上面这句就是去判断是否执行onSaveInstanceState的回调的。可以看出在3.0以上的平台上,saveState为false,那么就不会回调了。
源码确实是这样的,但是我在我的华为手机上测试的时候发现,即便我的app的target设置比3.0还大,依旧还是会回调该函数:
我是在华为d1上测试的,该机子系统版本是4.1.1 api为16,当我启动一个透明或是dialog样式的activity的时候,此时肯定不会回调onStop方法,打印生命周期是这样的:
07-11 16:00:59.106 21412-21412/com.txt.mydemo I/LifecycleActivity: onPause07-11 16:00:59.146 21412-21412/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged07-11 16:00:59.263 21412-21412/com.txt.mydemo I/LifecycleActivity: onSaveInstanceState
在执行完onPause后执行了onSaveInstanceState方法。可是上面不是说了吗,savaState为false的是不会回调。那是什么原因呢?
仔细看了下,可以发现该日志,onPause是在onSaveInstanceState之前回调 的。而源码中,我们的onPause是在onSaveInstanceState之后执行的,源码是先执行了saveState的判断,然后再执行
this.performPauseActivityIfNeeded(r, reason);
该方法会回调onPause方法,所以上面的源码分析是正确的。也就是说在其他地方可能也回调了onSaveInstanceState方法,目前暂时还没找到是在什么地方回调的。
有可能是第三方rom修改了逻辑呢。

2、在handleStopActivity中回调:
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;        }    }
该方法里面调用了performStopActivityInner,直接把saveState传了true,说明在调用onStop前必然有可能去回调onSaveInstanceState,这里为什么会说有可能呢?进入该方法看看
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);}
判断如果没有执行过finish(执行finish的时候mFinished就会被赋值),并且saveState为true的时候,就回调onSaveInstanceState了。从上面的逻辑中可以看出,saveState肯定为true,就看mFinished是否为false了。一般情况下,除非我们手动执行finish或是按返回键,这个值都为false状态,所以必然会去回调。
回调了该方法后,就接着执行performStop方法了,继而去执行onStop方法。
所以可以得出结论,在需要回调onSaveInstanceState的时候,必然是在onStop前回调的。


onRestoreInstanceState的调用时机呢?

其实之前一直以为该方法会和onSaveInstanceState成对出现,以至于被面试官问到的时候不知道怎么回答(可能我理解的面试官的意思是:既然每次进入后台都会回答onSaveInstanceState方法,那么onRestoreInstanceState必然也会调用,那么每次进入前台都要去从这里取数值吗?很有可能是理解有误,想当然了。。。)
事实上:
onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行。 
      另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。
从log上证明确实是这样的:
07-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onCreate sdk version: 2507-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onStart07-11 16:29:48.936 11766-11766/com.txt.mydemo I/LifecycleActivity: onResume07-11 16:29:49.006 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged07-11 16:29:52.326 11766-11766/com.txt.mydemo I/LifecycleActivity: onPause07-11 16:29:52.389 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged07-11 16:29:52.584 11766-11767/com.txt.mydemo E/dalvikvm: GC_CONCURRENT freed 8160K, 30% free 42805K/60679K, paused 20ms+2ms, total 48ms07-11 16:29:52.943 11766-11766/com.txt.mydemo I/LifecycleActivity: onSaveInstanceState07-11 16:29:52.943 11766-11766/com.txt.mydemo I/LifecycleActivity: onStop07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onRestart07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onStart07-11 16:29:56.248 11766-11766/com.txt.mydemo I/LifecycleActivity: onResume07-11 16:29:56.334 11766-11766/com.txt.mydemo I/LifecycleActivity: onWindowFocusChanged
按home键,执行了onSaveInstanceState方法,重新回到应用,并没有执行onRestoreInstanceState方法。

附上两个回调的实例,注意调用super的先后顺序:
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putBoolean("MyBoolean",true);
        savedInstanceState.putDouble("myDouble", 1.9);
        savedInstanceState.putInt("MyInt", 1);
        savedInstanceState.putString("MyString","Welcome back to Android");
        // etc.
        super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
}


后记:理解不透彻,难怪会被面试官问懵逼~~
阅读全文
0 0
原创粉丝点击