onSaveInstanceState和onRestoreInstanceState触发的时机

本文详细解析了Android中onSaveInstanceState方法的触发时机及其与onRestoreInstanceState的关系,包括具体实例及代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先看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会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
 

至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,(本人注:我昨晚调试时就发现原来不一定成对被调用的!)

onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执行

另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原。

至于这两个函数的使用,给出示范代码(留意自定义代码在调用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");
}

<think>嗯,用户遇到了一个关于Android平行视窗模式下焦点切换的问题。让我仔细想想这个问题的情况。当用户开启平行视窗后,右侧视窗获得焦点,然后灭屏再亮屏时,焦点却回到了左侧视窗。这可能是什么原因导致的呢? 首先,我需要回忆一下Android系统中Activity的生命周期焦点管理机制。当屏幕灭掉时,Activity会进入onPause状态,而亮屏后又会重新恢复。在平行视窗模式下,可能每个视窗对应不同的Activity实例或者同一个Activity的不同任务栈。 接下来,焦点管理方面。当用户点击右侧视窗时,系统应该记录当前获得焦点的窗口。灭屏后,系统可能保存了当前的窗口状态,但亮屏时可能没有正确恢复,导致焦点回到了默认的左侧视窗。这可能涉及到窗口管理器中焦点保存恢复的逻辑。 另外,平行视窗的实现方式可能因厂商而异。比如,华为的平行视窗小米的MIUI可能处理方式不同。需要确认是否在系统层面对多窗口焦点的处理存在缺陷,或者在灭屏时系统没有正确保存当前的焦点窗口信息。 可能的解决方向包括:检查Activity的onSaveInstanceStateonRestoreInstanceState方法是否正确保存恢复焦点状态;查看WindowManager中焦点相关的逻辑,确认灭屏事件是否触发了焦点的重置;或者检查系统服务中多窗口管理的部分,是否有在屏幕重新亮起时错误地重置焦点到默认窗口。 另外,可能需要自定义焦点恢复逻辑,在onResume或onWindowFocusChanged方法中主动请求焦点,或者拦截灭屏亮屏事件,手动保存恢复焦点状态。如果系统本身存在缺陷,可能需要厂商提供补丁或者寻找其他workaround。 最后,用户可能需要通过日志分析,查看灭屏亮屏过程中焦点变化的具体流程,找到焦点被错误重置的位置,进而修改相应的代码逻辑。例如,在亮屏时检查当前应获得焦点的窗口,并主动将焦点赋予右侧视窗。</think>这个问题涉及Android多窗口模式下的焦点管理机制,以下是逐步分析及解决方案: 1. **焦点保存机制缺陷**: - 灭屏时系统会触发onSaveInstanceState(),但默认可能不会保存窗口焦点状态 - 当前实现可能依赖View的isFocused()标记,但窗口重建时未正确恢复 - 检查点:查看ViewTree的focus存储是否在onSaveInstanceState()中被保存 2. **窗口堆栈管理**: ```java // 系统原生代码可能存在的处理逻辑 protected void onResume() { super.onResume(); if (mLastFocusedWindow != null) { mLastFocusedWindow.requestFocus(); // 可能错误地指向默认窗口 } } ``` 3. **解决方案**: (1) 自定义焦点保存: ```kotlin override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) val focusedView = currentFocus?.id ?: View.NO_ID outState.putInt("FOCUSED_VIEW_ID", focusedView) } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) val viewId = savedInstanceState.getInt("FOCUSED_VIEW_ID") findViewById<View>(viewId)?.post { it.requestFocus() } } ``` (2) 窗口状态监听: ```java // 在WindowManager中注册回调 window.decorView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { override fun onViewAttachedToWindow(v: View) { if (isRightWindowActive) { rightWindow.requestFocus() } } //... }); ``` (3) Power键事件拦截: ```java @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { persistWindowFocusState(); // 持久化当前焦点窗口状态 return true; } return super.dispatchKeyEvent(event); } ``` 4. **系统层修改建议**: - 修改PhoneWindowManager.java中亮屏处理逻辑: ```java void updateFocusAfterScreenOn() { WindowManagerInternal wm = LocalServices.getService(WindowManagerInternal.class); wm.updateFocusedWindowLocked( WindowManager.UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/); } ``` 5. **兼容性处理**: ```xml <!-- 在窗口声明中添加焦点保持属性 --> <activity android:name=".SplitWindowActivity" android:keepScreenOn="true" android:focusableInTouchMode="true" android:windowSoftInputMode="stateAlwaysVisible"/> ``` 建议通过以下步骤验证: 1. 使用`adb shell dumpsys window windows`查看焦点窗口状态 2. 检查ViewRootImpl中的mFocusedView赋值时机 3. 监控InputManagerService的焦点变化事件 该问题的本质是系统在屏幕状态变化时未正确处理多窗口场景的焦点恢复,需要结合应用层状态保存系统层焦点管理进行联合修正。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值