zoukankan      html  css  js  c++  java
  • Activity横竖屏切换生命周期

     Activity横竖屏切换生命周期

    /**
    * onCreate : 创建activity时调用。设置在该方法中,还以Bundle中可以提出用于创建该 Activity 所需的信息
    * onStart : activity变为在屏幕上对用户可见时,即获得焦点时,会调用
    * onResume : activity开始与用户交互时调用(无论是启动还是重新启动一个活动,该方法总是被调用的)
    * onPause : activity被暂停或收回cpu和其他资源时调用,该方法用于保存活动状态的
    * onStop : activity被停止并转为不可见阶段及后续的生命周期事件时,即失去焦点时调用
    * onDestroy : activity被完全从系统内存中移除时调用,该方法被调用可能是因为有人直接调用 finish()方法 或者系统决定停止该活动以释放资源
    *
    * onSaveInstanceState : 不是生命周期方法,只有在由系统销毁一个Activity时,会被调用
    * onRestoreInstanceState : 不是生命周期方法,只有在activity被系统回收,重新创建activity的情况下才会被调用
    * onConfigurationChanged : 不是生命周期方法,当系统的配置信息发生改变时,系统会调用此方法
    *
    */

    一、屏幕横竖屏切换的代码

    竖屏

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

    横屏

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    获得当前屏幕状态

    getResources().getConfiguration().orientation

    此状态可为:Configuration.ORIENTATION_PORTRAITConfiguration.ORIENTATION_LANDSCAPE

    二、横竖屏切换Activity生命周期回调

    横竖屏属性可以在AndroidManifest.xml中设置,也可以在MainActivity.java中进行设置(上面已经提及)。

    本节主要讲解在AndroidManifest.xml中的设置

    <activity android:name=".MainActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:screenOrientation="portrait">
    </activity>

    (一)configChanges属性

    设置configChanges这个值可以避免Activity生命周期被回到。该部分具体的参数:

    • orientation:屏幕在纵向和横向间旋转
    • keyboardHidden:键盘显示或隐藏
    • screenSize:屏幕大小改变
    • fontScale:用户变更了首选字母大小
    • locale:用户选择了不同的语言设定
    • keyboard:键盘类型变更,如手机从九宫格键盘变为全键盘
    • touchscreen或navigation:键盘或导航方向变换,一般不会发生这种情况。

    前面三个是常用的,后面属性很少使用

    如果要activity中的生命周期不回调,就要设置:

    android:configChanges="orientation|keyboardHidden|screenSize"

    缺少其中任一一个都会Activity生命周期回调,即如下情况:

    不发生回调是如下情况:

    在这附上对应的代码:

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private String TAG = "MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Log.i(TAG, "onCreate");
    
            Button bt = (Button)findViewById(R.id.bt_skip_other_activity);
            bt.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    else
                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                }
            });
        }
    
        @Override
        public void onConfigurationChanged(@NonNull Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            Log.i(TAG, "onConfigurationChanged");
            initChange();
        }
    
        private void initChange(){
            if(getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
                Log.i(TAG, "屏幕改变,当前为竖屏");
            else
                Log.i(TAG, "屏幕改变,当前为横屏");
        }
    
        @Override
        protected void onRestart() {
            super.onRestart();
            Log.i(TAG, "onRestart");
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            Log.i(TAG, "onStart");
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            Log.i(TAG, "onStop");
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.i(TAG, "onDestroy");
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            Log.i(TAG, "onPause");
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            Log.i(TAG, "onResume");
        }
    
        @Override
        protected void onSaveInstanceState(@NonNull Bundle outState) {
            super.onSaveInstanceState(outState);
            Log.i(TAG, "onSaveInstanceState");
        }
    
        @Override
        protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
            super.onRestoreInstanceState(savedInstanceState);
            Log.i(TAG, "onRestoreInstanceState");
        }
    }

    onSaveInstanceState() 和 onRestoreInstanceState() 方法

    现在我们再回过头来看这两个方法。

    1. 基本作用:

      首先要声明的是:Activity的 onSaveInstanceState() 和 onRestoreInstanceState() 并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,并不一定会被触发。当应用遇到意外情况(如:内存不足、按Home键等)由系统销毁一个Activity时,onSaveInstanceState() 会被调用。但当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState() 就不会被调用。此时,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState() 只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。

      比如说,当我们想在切换横竖屏的时候保存视频的播放进度。这时我们就可以在 Activity 中重写 onSaveInstanceState(Bundle outState) 方法。然后调用 outState.putXXX() 来保存数据。然后在 Activity 重新被创建时在 onCreate(Bundle savedInstanceState) 或 onRestoreInstanceState(Bundle savedInstanceState) 中 调用 savedInstanceState.getXXX() 来获取数据。 

      这就是 onSaveInstanceState() 和 onRestoreInstanceState() 两个函数的基本作用和用法了。

    2. onSaveInstanceState() 什么时候调用

      先看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变得容易被系统销毁前,会回调 onSaveInstanceState() 方法,除非该 activity 是被用户主动销毁的(比如按下BACK键)。

      何为"容易"? 有这么几种情况:

      (1)、当用户按下HOME键时。

      这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,因此系统会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则

      (2)、长按HOME键,选择运行其他的程序时。

      (3)、按下电源按键(关闭屏幕显示)时。

      (4)、从 activity A 中启动一个新的 activity 时。

      (5)、屏幕方向切换时,例如从竖屏切换到横屏时。

      在屏幕切换之前,系统会销毁 activity A,在屏幕切换之后系统又会自动地创建 activity A,所以onSaveInstanceState()一定会被执行,且也一定会执行onRestoreInstanceState()。

      总而言之,onSaveInstanceState()的调用遵循一个重要原则,即当系统存在 “未经你许可” 销毁了我们的 activity 的可能时,则 onSaveInstanceState() 会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据。且调用将发生在 onPause() 之前。

    3. onRestoreInstanceState()什么时候调用

      onRestoreInstanceState() 被调用的前提是,activity A “确实” 被系统销毁了,且 activity A 被重新创建。当 activity A 未被重新创建时,该方法不会被调用。例如,当正在显示 activity A 的时候,用户按下 HOME 键回到主界面,然后用户紧接着又返回到 activity A,这种情况下 activity A 一般不会因为内存的原因被系统销毁,故 onRestoreInstanceState() 方法不会被执行, 这也证明这两个方法不一定会成对被使用。

      onRestoreInstanceState() 在 onStart() 和 onResume() 之间调用。

    4. onSaveInstanceState()方法的默认实现

      如果我们没有覆写 onSaveInstanceState() 方法, 此方法的默认实现会自动保存 activity 中的某些状态数据, 比如 activity 中各种 UI 控件的状态.。android 应用框架中定义的几乎所有 UI 控件都恰当的实现了 onSaveInstanceState() 方法,因此当 activity 被摧毁和重建时, 这些 UI 控件会自动保存和恢复状态数据. 比如 EditText 控件会自动保存和恢复输入的数据,而 CheckBox 控件会自动保存和恢复选中状态.开发者只需要为这些控件指定一个唯一的 ID (通过设置 android:id 属性即可), 剩余的事情就可以自动完成了.如果没有为控件指定 ID, 则这个控件就不会进行自动的数据保存和恢复操作。

      由上所述, 如果我们需要覆写onSaveInstanceState()方法, 一般会在第一行代码中调用该方法的默认实现:super.onSaveInstanceState(outState)。

    5. 是否需要重写onSaveInstanceState()方法

      既然该方法的默认实现可以自动的保存UI控件的状态数据, 那什么时候需要覆写该方法呢?

      如果需要保存额外的数据时, 就需要覆写onSaveInstanceState()方法。大家需要注意的是:onSaveInstanceState()方法只适合保存瞬态数据, 比如UI控件的状态, 成员变量的值等,而不应该用来保存持久化数据,如果当用户离开当前 activity 时需要保存数据,应该在 onPause() 中保存(比如将数据保存到数据库或文件中)。且 onPause() 中不适合做耗时的操作。

      由于onSaveInstanceState()方法方法不一定会被调用, 因此不适合在该方法中保存持久化数据, 例如向数据库中插入记录等. 保存持久化数据的操作应该放在onPause()中。若是永久性值,则在onPause()中保存;若大量,则另开线程吧,别阻塞UI线程。

    6. 引发activity销毁和重建的其它情况

      除了系统处于内存不足的原因会摧毁 activity 之外, 某些系统设置的改变也会导致 activity 的摧毁和重建. 例如改变屏幕方向(见上), 改变语言设定, 键盘弹出等。


    android:configChanges 属性

    VALUE                   DESCRIPTION  
    
    "mcc"                   国际移动用户识别码所属国家代号是改变了
    "mnc"                   国际移动用户识别码的移动网号码是改变了
    "locale"                地址改变了-----用户选择了一个新的语言会显示出来
    "touchscreen"           触摸屏是改变了------通常是不会发生的
    "keyboard"              键盘发生了改变----例如用户用了外部的键盘
    "keyboardHidden"        键盘的可用性发生了改变
    "navigation"            导航发生了变化-----通常也不会发生
    "screenLayout"          屏幕的显示发生了变化------不同的显示被激活
    "fontScale"             字体比例发生了变化----选择了不同的全局字体 
    "uiMode"                用户的模式发生了变化
    "orientation"           屏幕方向改变了
    "screenSize"            屏幕大小改变了
    "smallestScreenSize"    屏幕的物理大小改变了,如:连接到一个外部的屏幕上

      以上是 android:configChanges 属性的所有值。当我们希望一种或者多种配置改变时避免重新启动 activity。就可以通过在 AndroidManifest 中设置 android:configChanges 属性来实现。如下所示:

    <activity
        android:name=".XXXActivity"
        android:configChanges="XXX|XXX"/>

      我们可以在这里声明 activity 可以处理的任何配置改变,当这些配置改变时不会重新启动activity,而会调用 onConfigurationChanged() 方法。如果改变的配置中包含了你所无法处理的配置(在android:configChanges并未声明),你的 activity 仍然要被重新启动。

      当 Configuration 改变后,ActivityManagerService 将会发送"配置改变"的广播,会要求 ActivityThread 重新启动当前 focus 的 Activity。但当我们为 activity 配置了 configChanges 属性,那么 activity 就不会被销毁再重新创建,而是会回调 onConfigurationChanged 方法。

    ================== End

  • 相关阅读:
    Redis "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk"问题的解决
    素描
    python 标准库
    Python内置函数filter, map, reduce
    Python的lambda表达式
    python中super关键字的用法
    python一个注意的地方
    python中self,cls
    python中的实例方法、静态方法、类方法、类变量和实例变量
    python模块及包的导入
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/13414171.html
Copyright © 2011-2022 走看看