Android屏幕旋转后的变更--ConfigChange

本文详细介绍了Android中屏幕旋转时Activity的生命周期变化,包括正常生命周期、重建过程、数据丢失问题的解决方案以及如何通过onSaveInstanceState和onRestoreInstanceState方法保存和恢复数据。同时探讨了在Fragment存在时的生命周期变化,并提供了防止页面重建的方法,如使用android:configChanges和onConfigurationChanged。此外,还给出了相关源码示例。


在android开发中,有一个容易被忽视但其实很重要的问题:屏幕旋转后的页面重建。
本文将介绍下 当屏幕旋转后,页面生命周期的变化以及 如何防止页面重建带来的问题

1. Activity生命周期的变化

1.1 正常生命周期

一个Activity从创建到退出,正常的生命周期流程是:

onCreate-->onStart-->onResume-->onPause-->onStop-->onDestroy

1.2 屏幕旋转后重建Activity

当屏幕旋转时,Android系统会自动将当前屏幕方向Activity销毁,再重新创建一个适应新屏幕方向的Activity
很自然的,我们能猜到会执行onPause-->onStop-->onDestroy-->onCreate-->onStart-->onResume这些方法。但除了这些Android还为我们考虑到了数据丢失的问题。

试想一下这种场景:页面中有一个TextView和一个Button,每点击一次Button,TextView显示点击的次数。当点击了一些次数后,旋转屏幕,你会发现TextView上记录的次数恢复到了最开始的状态。
原因也不难解释:因为整个Activity销毁了,重新创建的Activity是一个最开始的状态。

1.3 解决数据丢失问题–onSaveInstanceState和onRestoreInstanceState

为了处理这种由于销毁重建导致的页面数据丢失的问题,Android提供了另外两个方法 onSaveInstanceStateonRestoreInstanceState,算是对生命周期方法的补充吧。

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
   
   
    super.onSaveInstanceState(outState);
    Log.d(TAG, "onSaveInstanceState: ");

}

@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
   
   
    super.onRestoreInstanceState(savedInstanceState);
    Log.d(TAG, "onRestoreInstanceState: ");
}

注意:onSaveInstanceState有两个重载方法,带outPersistentState参数的是针对那种重启手机的情况的,本文我们暂时不考虑,也不会回调。我们只关注第二个不带outPersistentState参数的就行了。
@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);

}

从名字上也可以看出,onSaveInstanceState是对状态的保存,onRestoreInstanceState是对状态的恢复。

1.4 整个屏幕旋转过程调用的生命周期方法

了解到这些,当屏幕旋转后回调的方法最终是这样的:

onPause-->onStop-->onSaveInstanceState-->onDestroy-->onCreate-->
onStart-->onRestoreInstanceState-->onResume

没错,onSaveInstanceState在onDestroy之前;onRestoreInstanceState在onResume之前。

1.5 onCreate方法中的savedInstanceState参数

除此之外,我们还应了解到,我们最常见的onCreate方法中其实有一个参数savedInstanceState。一直以来很少用到它,但现在终于轮到它发挥作用了。它就是之前销毁Activity时保存的数据,这样以来其实不用等到onRestoreInstanceState方法回调,在onCreate时我们就能拿到之前保存的数据了。

@Override
protected void onCreate(Bundle savedInstanceState) {
   
   
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_config_change_test);
    if (savedInstanceState != null) {
   
   
        // 页面重建了
        Log.d("TAG", "onCreate: savedInstanceState:" + savedInstanceState);
    }
}

再仔细观察下,savedInstanceState、outState这些参数都是Bundle类型的哦,也就是说他们互相传递毫无障碍,都是用来盛放键值对儿的。

1.6 onSaveInstanceState的调用时机

经过测试,以下情况都会调用onSaveInstanceState方法

  • 屏幕旋转
  • 锁屏
  • 按home键回到桌面
  • ActivityA 跳到 ActivityB,调ActivityA的onSaveInstanceState

而正常的退出Activity是不会调用onSaveInstanceState的

  • 按返回键退出Activity
  • 调用finish方法退出Activity

可见,onSaveInstanceState 就是在那些用户期望保留数据的场景才会调用,这也是它存在的初衷。

1.7 注意事项

  • onSaveInstanceState方法只适合保存少量的、不复杂的数据,如少量String,boolean等。
    因为它涉及到序列化反序列化等操作,如果数据复杂会比较耗时,导致页面卡顿。

2. 存在Fragment时的生命周期变化

除了Activity,我们还应该了解到Fragment在屏幕旋转过程也会自动销毁重建。看过Fragment的基础与应用系列文章的伙伴都知道,Fragment是依附在Activity上的,可以说是一些视图控件View组成的“片段”页面。当Activity都销毁了,那它上面的View肯定也都销毁了。相应的,Activity的View树恢复了,它自然也得把上面的Fragment恢复。在Activity看来,Fragment就是它上面的一些View构成的集合而已,当然要一视同仁的恢复了。

这里直接给出依次调用的生命周期方法。文末会给出源码,你也可以自己写小例子测试。

 Fragment: onPause:
 TestActivity: onPause:
 Fragment: onStop:
 TestActivity: onStop:
 Fragment: onSaveInstanceState:
 TestActivity: onSaveInstanceState:
 Fragment: onDestroyView:
 Fragment: onDestroy:
 Fragment: onDetach:
 TestActivity: onDestroy:
 Fragment: Fragment 构造方法:
 Fragment: onAttach:
 Fragment: onCreate: savedInstanceState:非空
 TestActivity: onCreate: savedInstanceState:非空
 Fragment: onCreateView:
 Fragment: onViewCreated:
 Fragment: onActivityCreated:
 Fragment: onViewStateRestored:
 Fragment: onStart:
 TestActivity: onStart:
 TestActivity: onRestoreInstanceState:
 TestActivity: onResume:
 Fragment: onResume:

可见,在Activity销毁重建的同时,其上的Fragment也进行了类似的过程,也存在保存和恢复数据的方法:

Fragment: onSaveInstanceState:
Fragment: onViewStateRestored:

3. 如何防止页面重建

那么屏幕旋转后一定非要页面重建吗?当然不是,Android尽可能的为我们提供了选择。

3.1 android:configChanges

如果你不想让页面的Activity销毁重建的话,可以在AndroidManifest.xml文件的Activity节点里添加android:configChanges配置,如下:

<activity
    android:name=".config_change.ConfigChangeTestActivity"
    android:configChanges="orientation|screenSize" />

android:configChanges的值代表了哪些配置发生变化时页面不必重建。上述配置代码的orientation|screenSize意思是说,方向 | 屏幕大小 发生变化时页面不重建。

注意:经过本人测试,这里必须同时配置orientation|screenSize这两个值才能阻止页面重建,只配置一个orientation或者screenSize都是不行的。

另外,还有一些其他值:screenLayout|keyboardHidden等,有兴趣可以自行了解。

3.2 onConfigurationChanged

配置完android:configChanges后,旋转屏幕你会发现,虽然页面旋转了,但Activity的生命周期方法没有调用,也就是页面没有销毁重建。目的达到了,但真的万事大吉了吗?

事实上,Android官方是不推荐我们添加这个阻止自动重建的配置的。仔细想想,为什么它会设计成默认自动重建,就是因为在屏幕方向或者大小发生变化时,页面所依赖的尺寸等值也不一样了。如果不重建,就意味着你可能会将竖屏时候的值应用给旋转后的横屏Activity,这样很难说不会出问题。除非你自己完成屏幕旋转后的适配工作,而这个是比较难以考虑周全的。

但A

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子林Android

感谢老板,老板大气!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值