/**
* 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_PORTRAIT
和Configuration.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