横竖屏切换 屏幕适配 状态保存与恢复 [MD]

博文地址

我的GitHub 我的博客 我的微信 我的邮箱
baiqiantao baiqiantao bqt20094 baiqiantao@sina.com

目录

横竖屏切换适配

Android开发中,大多APP可能根据实际情况直接将APP的界面方向设死了,或竖屏或横屏。但是,我们还是会遇到横竖屏切换的功能需求,不管是通过物理重力感应触发,还是用户手动触发。所以,我们有必要去弄清楚Android中横竖屏切换到底做了什么。

小技巧

  • 可以在开发中选项中修改最小宽度的值(单位是dp),这样就可以真实模拟其他分辨率的手机(或pad)
  • 可以layout-landlayout-port中放同名的布局文件
    • 对于重启 activity 模式:当横竖屏切换时会自动使用不同的布局
    • 对于非重启 activity 模式:需要在 onConfigurationChanged 中重新 setContentView
  • 对于 RecyclerView,可以在 onConfigurationChanged 中根据横竖屏状态设置不同的 LayoutManager 来改变布局样式

官方文档:两个属性和三个方法

属性 android:screenOrientation

用来设置 activity 在设备上的显示方向

android:screenOrientation="portrait"
可选值 含义
landscape(常用) 屏幕方向为横向(显示的宽度大于高度)。
portrait(常用) 屏幕方向为纵向(显示的高度大于宽度)。
sensor(常用) 屏幕方向由设备方向传感器决定。显示方向取决于用户如何手持设备,它会在用户旋转设备时发生变化。但在默认情况下,一些设备不会旋转为所有四种可能的方向。如要支持所有这四种方向,请使用 "fullSensor"。即使用户锁定基于传感器的旋转,系统仍可使用传感器。
fullSensor(常用) 屏幕方向由使用 4 种方向中任一方向的设备方向传感器决定。这与 "sensor" 类似,不同之处在于无论设备在正常情况下使用哪种方向,该值均支持所有 4 种可能的屏幕方向(例如,一些设备正常情况下不使用反向纵向或反向横向,但其支持这些方向)。API9 新增
unspecified 默认值。由系统选择方向。在不同设备上,系统使用的政策以及基于政策在特定上下文中所做的选择可能会有所差异。
behind 与 Activity 栈中紧接其后的 Activity 的方向相同。
reverseLandscape 屏幕方向是与正常横向方向相反的横向。API9 新增
reversePortrait 屏幕方向是与正常纵向方向相反的纵向。API9 新增
sensorLandscape 屏幕方向为横向,但可根据设备传感器调整为正常或反向的横向。即使用户锁定基于传感器的旋转,系统仍可使用传感器。API9 新增
sensorPortrait 屏幕方向为纵向,但可根据设备传感器调整为正常或反向的纵向。即使用户锁定基于传感器的旋转,系统仍可使用传感器。API9 新增
userLandscape 屏幕方向为横向,但可根据设备传感器和用户首选项调整为正常或反向的横向。API18 新增
userPortrait 屏幕方向为纵向,但可根据设备传感器和用户首选项调整为正常或反向的纵向。API18 新增
nosensor 确定屏幕方向时不考虑物理方向传感器。系统会忽略传感器,因此显示内容不会随用户手持设备的方向而旋转。
user 用户当前的首选方向。
fullUser 如果用户锁定基于传感器的旋转,则其行为与 user 相同,否则,其行为与 fullSensor 相同,并且支持所有 4 种可能的屏幕方向。API18 新增
locked 将屏幕方向锁定为其当前的任意旋转方向。API18 新增

属性 android:configChanges

列出 Activity 将自行处理的配置变更集合,不仅仅是屏幕方向,还有语言、地区等等。

android:configChanges="keyboardHidden|screenSize|orientation"

在运行时发生配置变更时,默认情况下会关闭 Activity 并将其重启,但使用该属性声明配置将阻止 Activity 重启。相反,Activity 会保持运行状态,并且系统会调用其 onConfigurationChanged() 方法。

请注意:应避免使用该属性,并且只应在万不得已的情况下使用。如需了解有关如何正确处理配置变更所致重启的详细信息,请阅读 处理运行时变更

可选值 含义
keyboardHidden(常用) 键盘无障碍功能发生变更 — 例如,用户显示硬键盘。
orientation(常用) 屏幕方向发生变更 — 用户旋转设备。请注意:如果应用面向 API13 或更高版本的系统,则还应声明 screenSize 配置,因为当设备在横向与纵向之间切换时,该配置也会发生变更。
screenSize(常用) 当前可用屏幕尺寸发生变更。该值表示当前可用尺寸相对于当前纵横比的变更,当用户在横向与纵向之间切换时,它便会发生变更。API13 新增
density 显示密度发生变更 — 用户可能已指定不同的显示比例,或者有不同的显示现处于活跃状态。API24 新增
fontScale 字体缩放系数发生变更 — 用户已选择新的全局字号。
keyboard 键盘类型发生变更 — 例如,用户插入外置键盘。
layoutDirection 布局方向发生变更 — 例如,自从左至右 (LTR) 更改为从右至左 (RTL)。API17 新增
locale 语言区域发生变更 — 用户已为文本选择新的显示语言。
mcc IMSI 移动设备国家/地区代码 (MCC) 发生变更 — 检测到 SIM 并更新 MCC。
mnc IMSI 移动设备网络代码 (MNC) 发生变更 — 检测到 SIM 并更新 MNC。
navigation 导航类型(轨迹球/方向键)发生变更。(这种情况通常不会发生。)
screenLayout 屏幕布局发生变更 — 不同的显示现可能处于活跃状态。
smallestScreenSize 物理屏幕尺寸发生变更。该值表示与方向无关的尺寸变更,因此它只有在实际物理屏幕尺寸发生变更(如切换到外部显示器)时才会变化。对此配置所作变更对应 smallestWidth 配置的变化。API13 新增
touchscreen 触摸屏发生变更。(这种情况通常不会发生。)
uiMode 界面模式发生变更 — 用户已将设备置于桌面或车载基座,或者夜间模式发生变更。API8 新增

所有这些配置变更都可能影响应用所看到的资源值。因此,调用 onConfigurationChanged() 时,通常有必要再次检索所有资源(包括视图布局、可绘制对象等),以正确处理变更。

方法 onConfigurationChanged

这几个方法都是 Activity 中的

public void onConfigurationChanged (Configuration newConfig)
    //Configuration newConfig: The new device configuration. This value cannot be null.

Called by the system when the device configuration changes while your activity is running. Note that this will only be called if you have selected android:configChanges you would like to handle with the R.attr.configChanges attribute in your manifest. If any configuration change occurs that is not selected to be reported by that attribute, then instead of reporting it the system will stop and restart the activity (to have it launched with the new configuration).

At the time that this function has been called, your Resources object will have been updated to return resource values matching the new configuration.

方法 onSaveInstanceState

public void onSaveInstanceState (Bundle outState)
    //Bundle outState: Bundle in which to place your saved state. This value cannot be null.

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).

在 activity 被终止之前调用,以从中取回每个实例的状态,以便可以在 ... 中恢复状态(此方法填充的 Bundle 将传递给两者)。

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 可能被杀死之前被调用,这样当它在未来某个时间回来时它可以恢复它的状态。例如,如果 activityB 在 activityA 之前启动,并且在某个时刻 activityA 被杀死以回收资源,则 activityA 将有机会通过此方法保存其用户界面的当前状态,以便当用户返回时对于 activityA,用户界面的状态可以通过 ... 恢复。

Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when the user no longer actively interacts with an activity, or onStop() which is called when activity becomes invisible. 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 生命周期回调混淆,例如,当用户不再主动与 activity 交互时总是调用 onPause(),或者当 activity 变得不可见时调用 onStop()。调用 onPause() 和 onStop() 而不调用此方法的一个示例是,当用户从 activityB 导航回 activityA 时:此时无需在 B 上调用 onSaveInstanceState(Bundle) 因为该特定实例永远不会恢复,所以系统会避免调用它。调用 onPause() 而不是 onSaveInstanceState(Bundle) 的示例是当 activityB 在 activityA 之前启动时:如果 activityA 在 activityB 的生命周期内没有被杀死,系统会避免在 activityA 上调用 onSaveInstanceState(Bundle),A 的用户界面的状态将保持不变。

The default implementation takes care of most of the UI per-instance state for you by calling View.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.

默认实现通过在层次结构中具有 id 的每个 View 上调用 View.onSaveInstanceState() 并保存当前聚焦的 View 的 id 来为您处理大部分 UI 每个实例状态,所有这些都是通过 onRestoreInstanceState(Bundle) 的默认实现来恢复的。如果您重写此方法以保存每个单独 View 未捕获的其他信息,您可能希望调用默认实现,否则就要准备好自己保存每个视图的所有状态。

If called, this method will occur after onStop() for applications targeting platforms starting with Build.VERSION_CODES.P(28). For applications targeting earlier platform versions this method will occur before onStop() and there are no guarantees about whether it will occur before or after onPause().

如果调用,此方法将在 onStop() 之后针对...。对于面向早期平台版本的应用程序,此方法将在 onStop() 之前发生,并且无法保证它是在 onPause() 之前还是之后发生。

方法 onRestoreInstanceState

protected void onRestoreInstanceState (Bundle savedInstanceState)
    //Bundle: the data most recently 最近 supplied in onSaveInstanceState(Bundle). This value cannot be null.

This method is called after onStart() when the activity is being re-initialized from a previously saved state, given here in savedInstanceState. Most implementations will simply use onCreate(Bundle) to restore their state, but it is sometimes convenient to do it here after all of the initialization has been done or to allow subclasses to decide whether to use your default implementation. The default implementation of this method performs a restore of any view state that had previously been frozen by onSaveInstanceState(Bundle).

当 activity 从先前保存的状态重新初始化时,在 onStart() 之后调用此方法。大多数实现将简单地使用 onCreate(Bundle) 来恢复它们的状态,但有时在完成所有初始化后在这里执行它、或允许子类决定是否使用您的默认实现会很方便。此方法的默认实现会执行恢复先前被 onSaveInstanceState(Bundle) 保存的任何视图状态。

This method is called between onStart() and onPostCreate(Bundle). This method is called only when recreating an activity; the method isn't invoked if onStart() is called for any other reason.

该方法在 onStart()onPostCreate(Bundle) 之间调用。 此方法仅在重新创建 activity 时调用;如果出于任何其他原因调用 onStart() ,则不会调用该方法。

官方文档:如何处理 config change

某些设备配置可能会在运行时发生变化,例如屏幕方向、键盘可用性,以及当用户启用多窗口模式时。发生这种变化时,Android 会重启正在运行的 Activity(先后调用onDestroyonCreate)。重启行为旨在通过利用与新设备配置相匹配的备用资源,来自动重新加载您的应用,从而帮助它适应新配置。

然而,重启应用并恢复大量数据不仅成本高昂,而且会造成糟糕的用户体验。在此情况下,您还有两个选择:

1、在配置变更期间保留对象,并使用 ViewModel 等方式保存界面数据

允许 Activity 在配置变更时重启,但是需将有状态对象传递给 Activity 的新实例。

2、阻止重启 Activity,并在回调中自行处理配置变更

如果您无法使用首选项(onSaveInstanceState()、ViewModel 和持久存储)来保留界面状态,则可阻止系统在特定配置变更期间重启您的 Activity。配置变更时,应用会收到回调,以便您可以根据需要手动更新 Activity。

允许重启并自行保存界面数据

如果重启 Activity 需要恢复大量数据、重新建立网络连接或执行其他密集操作,那么因配置变更而引起的完全重启可能会给用户留下应用运行缓慢的体验。

此外,若使用系统通过 onSaveInstanceState() 回调为您保存的 Bundle,则可能 无法完全恢复 Activity 状态,因为该类并非用于携带大型对象(例如Bitmap),并且其中的数据必须依次在主线程中进行序列化和反序列化,而这可能会消耗大量内存并降低配置变更的速度。

在此情况下,您可通过使用 ViewModel 对象来减轻重新初始化 Activity 的负担。系统会在配置变更时保留 ViewModel,使其成为保存界面数据的理想场所,让您无需再次查询这些数据。

阻止重启并自行处理配置变更

如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免 Activity 重启,则可声明 Activity 自行处理配置变更,从而阻止系统重启 Activity。

注意:自行处理配置变更可能会提高使用备用资源的难度,因为系统不会为您自动应用这些资源。只有在必须避免 Activity 因配置变更而重启的无奈情况下,您才可考虑使用此方法,并且不建议对大多数应用使用此方法。

如要声明由 Activity 处理配置变更,请在清单文件中编辑相应的元素,以包含 android:configChanges 属性,该属性的值表示要处理的配置。您可以在属性中声明多个配置值,方法是用管道 | 字符将其进行分隔。

常用的值:

  • orientation 值可在屏幕方向发生变更时阻止重启
  • screenSize 值也可在屏幕方向发生变更时阻止重启,但仅适用于 API13 及以上版本的系统。若想在应用中手动处理配置变更,您必须在 android:configChanges 属性中声明 orientationscreenSize
  • keyboardHidden 值可在键盘可用性发生变更时阻止重启。

现在,即便其中某个配置发生变化, Activity 也不会重启,但 Activity 会接收到对 onConfigurationChanged() 的调用消息。此方法会收到传递的 Configuration 对象,从而指定新设备配置。您可以通过读取 Configuration 中的字段确定新配置,然后通过更新界面所用资源进行适当的更改。调用此方法时,Activity 的 Resources 对象会相应地进行更新,并根据新配置返回资源,以便您在系统不重启 Activity 的情况下轻松重置界面元素。

Configuration 对象代表所有当前配置,而不仅仅是已变更的配置。多数情况下,您并不在意配置具体发生了哪些变更,而且您可以轻松地重新分配所有资源,为正在处理的配置提供备用资源。例如,由于 Resources 对象现已更新,您便可通过 setImageResource() 重置任何 ImageView 并使用合适的新配置资源。

请谨记:在声明由 Activity 处理配置变更时,您有责任 resetting any elements for which you provide alternatives(备用资源)。如果您声明由 Activity 处理方向变更,且需要在横向和纵向之间切换某些图像,则您必须在 onConfigurationChanged() 期间为每个元素 re-assign(重新分配) each resource。

如果无需根据这些配置变更更新应用,则您可不必实现 onConfigurationChanged()。在此情况下,all of the resources used before the configuration change are still used,区别在于您无需重启 Activity。但是,您的应用应始终能在保持先前状态完好的情况下关闭和重启,因此您不应该认为,使用此方法即可无需保留正常 Activity 生命周期中的状态。其原因是一些其他配置变更会强制重启应用,而且某些事件需由您进行处理,例如以下情况:when the user leaves your application and it gets destroyed before the user returns to it.

横竖屏切换下的生命周期

不配置 screenSize 场景

下面三种方式 Log是一样的:横竖屏切换时会重新调用各个生命周期,且都是执行一次生命周期方法

  • 不配置 android:configChanges
  • 仅配置 android:configChanges="orientation"
  • 仅配置 android:configChanges="orientation|keyboardHidden"
//竖屏切横屏
onPause
onSaveInstanceState
onStop
onDestroy
onCreate - orientation
onStart
onRestoreInstanceState
onResume
//横屏切竖屏
onPause
onSaveInstanceState
onStop
onDestroy
onCreate - orientation
onStart
onRestoreInstanceState
onResume

我们经常在其他地方看到,以上3种不同配置下,回调 Activity 生命周期方法次数并不一致,为什么和我们的结论不一样呢?

其实是 Android 高、低版本的差异而已,查看官方文档,发现有如下提示:

注意:从 Android 3.2(API13)开始,当设备在纵向和横向之间切换时, screenSize 也会发生变化。因此,在开发针对 API13 或更高版本系统的应用时,若要避免由于设备方向改变而导致运行时重启,则除了orientation值以外,您还必须添加screenSize值。但是,如果您的应用是面向(注意,指的是targetSdkVersion) API12 或更低版本的系统,则 Activity 始终会自行处理此配置变更(注意,即便是在 Android 3.2 或更高版本的设备上运行,此配置变更也不会重启 Activity)。

目前不会还有兼容 API12 或更低版本的应用了,所以这三种配置都已经不适用了。

完整配置场景

android:configChanges="orientation|keyboardHidden|screenSize"
//竖屏切横屏
onConfigurationChanged

//横屏切竖屏
onConfigurationChanged

按照这种配置,横竖屏切换时才不会销毁 activity,且只调用 onConfigurationChanged方法。

代码中动态切换横竖屏

在代码中切换屏幕的方向主要调用 setRequestedOrientation(int requestedOrientation) 方法,此方法的作用等同于在 AndroidManifest.xml 设置的 android:screenOrientation

boolean isLandscape = getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
Toast.makeText(TestActivity.this, "当前是 " + (isLandscape ? "横屏" : "竖屏"), Toast.LENGTH_SHORT).show();
setRequestedOrientation(isLandscape ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

注意: 通过 setRequestedOrientation 修改了屏幕方向后,就类似于设置了 android:screenOrientation,效果是一样的。比如:调用 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) 后,无论屏幕怎么旋转,都不会切换屏幕方向。如果要恢复为响应横竖屏随物理方向传感器设备变换,那么就需要手动调用类似 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR)代码进行恢复。

适用场景:如果我们应用是手机和平板都可用的,但是手机上只能是竖屏不可切换,平板上只能是横屏不可切换,那么就可以在区分设备是手机还是平板后,在BaseActivityonCreate方法中通过setRequestedOrientation设置屏幕方向。

重启模式下数据的保存与恢复

在重启 Activity 模式下,横竖屏切换的时候会导致数据丢失,我们可以通过如下代码来保证数据不丢失:

public class TestActivity extends Activity {
    private String date;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView textView = new TextView(this);
        if (savedInstanceState != null) {
            date = savedInstanceState.getString("date"); //在 onCreate 中也是可以拿到之前保存的数据的
        } else {
            date = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date());
        }
        textView.setText(date);
        setContentView(textView);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        outState.putString("date", date); //putInt、putBoolean、putCharSequence、putIntArray、putIntegerArrayList...
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        if (savedInstanceState != null) {
            String date = savedInstanceState.getString("date");
            Toast.makeText(this, date, Toast.LENGTH_SHORT).show();
        }
    }
}

重启模式下的屏幕适配

重启模式下,如果大家在资源目录 res 中添加了 layout-land(横向布局文件夹) 和 layout-port(竖想布局文件夹),重启 Activity 模式的横竖屏切换,系统会自动帮我们显示正确方向的布局 UI。

非重启模式下的屏幕适配

非重启 Activity 模式下横竖屏切换时,我们的 Activity 不会销毁重建,数据也不会丢失。所以,如果按照上面的方式,在资源目录 res 中添加 layout-landlayout-port,会发现并没有效果,此时应该怎么做呢?

其实也很简单,只需要在onConfigurationChanged中再掉一次setContentView即可。

public class TestActivity extends Activity {
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.tv);
        mTextView.setText(new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date()));
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            setContentView(R.layout.activity_main);
            TextView textView = findViewById(R.id.tv);
            textView.setText("切换到了竖屏 " + (textView == mTextView)); 
            //注意1:界面上的 TextView 已经不是原先 onCreate 中的 TextView 了,必须重新find一次
            //注意2:由于 activity 没变,所以原先的数据都还存在,此时可以直接铺到新的 UI 上去
        } else {
            setContentView(R.layout.activity_main);
            TextView textView = findViewById(R.id.tv);
            textView.setText("切换到了横屏 " + (textView == mTextView));
        }
        mTextView.setText(new SimpleDateFormat("yyyy.MM.dd HH:mm:ss SSS", Locale.getDefault()).format(new Date())); //没有效果
    }
}

横竖屏切换对 Fragment 的影响

测试代码 Activity

public class TestActivity extends FragmentActivity {
    private static final String TAG = "child";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
            Log.d("Activity", " -- onCreate has no child ");
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
            transaction.add(R.id.root_view, BlankFragment.newInstance("baiqiantao"), TAG);
            transaction.commit();
        } else {
            Log.d("Activity", " -- onCreate has child ");
        }
    }
}

测试代码 Fragment

public class BlankFragment extends Fragment {
    private static final String ARGUMENT = "argument";
    private String mArgument;

    public static BlankFragment newInstance(String argument) {
        Bundle bundle = new Bundle();
        bundle.putString(ARGUMENT, argument);
        BlankFragment fragment = new BlankFragment();
        fragment.setArguments(bundle);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("Fragment", "-- onCreate");
        Bundle bundle = getArguments();
        if (bundle != null) {
            mArgument = bundle.getString(ARGUMENT);
        }
    }

    @Override
    public View onCreateView(@NotNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Log.i("Fragment", "-- onCreateView");
        TextView textView = new TextView(getActivity());
        textView.setBackgroundColor(Color.RED);
        textView.setText(mArgument);
        return textView;
    }
}

非重建 Activity 模式

横竖屏切换时,Fragment 和 Activity 都只会调用 onConfigurationChanged 方法,不会走其他生命周期的方法。

Fragment: -- onConfigurationChanged
Activity: -- onConfigurationChanged

重建 Activity 模式

带有 Fragment 即为 Fragment 的打印,其他则为 Activity 的打印

进入 Activity 的 Log

-- onCreate has no child
Fragment -- newInstance
Fragment -- onAttach
Fragment -- onCreate
Fragment -- onCreateView
Fragment -- onActivityCreated
Fragment -- onStart
-- onStart
-- onResume
Fragment -- onResume

横竖屏切换的Log

虽然 Fragment 不像 Activity 拥有 onRestoreInstanceState 方法,但是我们可以在 onActivityCreated 中获取之前保存的数据。

Fragment -- onPause
-- onPause
-- onSaveInstanceState save: name = bqt  //【Activity 保存数据】
Fragment -- onSaveInstanceState save str  //【Fragment 可以在此保存数据】
Fragment -- onStop
-- onStop
Fragment -- onDestroyView
Fragment -- onDestroy
Fragment -- onDetach
-- onDestroy
Fragment -- onAttach
Fragment -- onCreate  //【Fragment 重建了。一定注意,是 new 了一个新的对象,而不是复用旧的对象】
-- onCreate get: name = bqt  //【Activity 重建了且数据恢复了。同样,也是 new 了一个新的对象】
-- onCreate has child //【自动将新的 Fragment 添加进了新的 Activity】
Fragment -- onCreateView
Fragment -- onActivityCreated get  //【Fragment 可以在此获取之前保存的数据】
Fragment -- onStart
-- onStart
-- onRestoreInstanceState get: name = bqt
-- onResume
Fragment -- onResume

2021-6-2

原文地址:https://www.cnblogs.com/baiqiantao/p/14843044.html