测试手机:Nexus 5 系统:4.4
一、测试
测试代码:
1 package com.example.androidalarm; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.content.res.Configuration; 6 import android.os.Bundle; 7 import android.util.AttributeSet; 8 import android.util.Log; 9 import android.view.View; 10 import android.widget.Button; 11 12 public class MainActivity extends Activity { 13 Button addButton, cancelButton; 14 15 @Override 16 protected void onCreate(Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.activity_main); 19 Log.d("BigFootprint", "onCreate"); 20 } 21 22 @Override 23 public void onConfigurationChanged(Configuration newConfig) { 24 Log.d("BigFootprint", "onConfigurationChanged"); 25 super.onConfigurationChanged(newConfig); 26 } 27 28 @Override 29 public View onCreateView(String name, Context context, AttributeSet attrs) { 30 Log.d("BigFootprint", "onCreateView"); 31 return super.onCreateView(name, context, attrs); 32 } 33 34 @Override 35 protected void onDestroy() { 36 Log.d("BigFootprint", "onDestroy"); 37 super.onDestroy(); 38 } 39 40 @Override 41 protected void onPause() { 42 Log.d("BigFootprint", "onPause"); 43 super.onPause(); 44 } 45 46 @Override 47 protected void onRestart() { 48 Log.d("BigFootprint", "onRestart"); 49 super.onRestart(); 50 } 51 52 @Override 53 protected void onRestoreInstanceState(Bundle savedInstanceState) { 54 Log.d("BigFootprint", "onRestoreInstanceState"); 55 super.onRestoreInstanceState(savedInstanceState); 56 } 57 58 @Override 59 protected void onResume() { 60 Log.d("BigFootprint", "onResume"); 61 super.onResume(); 62 } 63 64 @Override 65 protected void onSaveInstanceState(Bundle outState) { 66 Log.d("BigFootprint", "onSaveInstanceState"); 67 super.onSaveInstanceState(outState); 68 } 69 70 @Override 71 protected void onStart() { 72 Log.d("BigFootprint", "onStart"); 73 super.onStart(); 74 } 75 76 @Override 77 protected void onStop() { 78 Log.d("BigFootprint", "onStop"); 79 super.onStop(); 80 } 81 }
XML配置:
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.androidalarm" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="18" /> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name="com.example.androidalarm.MainActivity" 18 android:label="@string/app_name" > 19 <intent-filter> 20 <action android:name="android.intent.action.MAIN" /> 21 <category android:name="android.intent.category.LAUNCHER" /> 22 </intent-filter> 23 </activity> 24 </application> 25 </manifest>
以以上的代码运行,打开应用,LOG输出如下“
可以看到,打开Activity的时候,生命周期是符合文档描述的,但是onCreateView接口会被多次调用,所以最好不要在这边实例化数据。按下BACK键退出,LOG如下:
生命周期非常正常。当Activity显示的时候,屏幕暗掉,LOG如下:
红框中为多打印的3个生命周期过程,可以看到onSaveInstanceState的调用时机!亮起屏幕,LOG如下:
生命周期一如文档所说。如果按下Home键退出,则LOG如下:
可以看到,和屏幕暗掉的LOG完全一样。重新点击应用图标打开Activity,得到如下LOG:
这个时候并没有去OnCreate,而是调用了方法onRestart方法。
这时候切换屏幕(竖屏切为横屏):(备注:因为onCreateView调用次数太多,而且不是重点研究对象,后面去掉此处LOG)
可以看到切换的时候,生命周期又走了一遍,并且调用了onSaveInstanceState和onRestoreInstanceState方法用于保存和恢复状态。然后从横屏恢复为竖屏,LOG如下:
调用的生命周期过程完全和上面的切换完全一样。
接下来要试验的是,重新在XML文件中配置Activity。
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.androidalarm" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="18" /> 10 11 <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission> 12 13 <application 14 android:allowBackup="true" 15 android:icon="@drawable/ic_launcher" 16 android:label="@string/app_name" 17 android:theme="@style/AppTheme" > 18 <activity 19 android:name="com.example.androidalarm.MainActivity" 20 android:configChanges="orientation" 21 android:label="@string/app_name" > 22 <intent-filter> 23 <action android:name="android.intent.action.MAIN" /> 24 <category android:name="android.intent.category.LAUNCHER" /> 25 </intent-filter> 26 </activity> 27 </application> 28 </manifest>
多加了一个配置: android:configChanges="orientation"。并且需要添加permission。
这是竖屏——>横屏——>竖屏切换过程中打印的LOG,可以看到,和没有配置的时候完全一样。再配置如下:添加: android:configChanges="orientation|keyboardHidden"
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.androidalarm" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="8" 9 android:targetSdkVersion="18" /> 10 11 <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"></uses-permission> 12 13 <application 14 android:allowBackup="true" 15 android:icon="@drawable/ic_launcher" 16 android:label="@string/app_name" 17 android:theme="@style/AppTheme" > 18 <activity 19 android:name="com.example.androidalarm.MainActivity" 20 android:configChanges="orientation|keyboardHidden" 21 android:label="@string/app_name" > 22 <intent-filter> 23 <action android:name="android.intent.action.MAIN" /> 24 <category android:name="android.intent.category.LAUNCHER" /> 25 </intent-filter> 26 </activity> 27 </application> 28 </manifest>
打印的LOG还是一样,接下来再配置:改成android:configChanges="orientation|screenSize", 切换横竖屏打印的LOG如下:
注意,这里也是竖屏——>横屏——>竖屏切换,每次切换只打印一行LOG。
二、结论
在网上看到很多的资料讲述生命周期,结论应该只有一个:生命周期会因为系统版本甚至因为定制等各种因素而各有差异。所以本文一开始就给出了测试环境。上面的测试只在一种环境下完成,不能代表所有系统和所有机型。读者如需了解问题,还应该在当前环境下自己去进行测试。但是由此依然可以得出一些结论:
1)onCreateView接口会被多次调用,在这个接口中的代码要好好考虑,实例化数据,加载资源的动作最好不要放在这个方法中;
2)如果不是程序主动关闭Activity(比如按下回退键),onPause()之后会调用onSaveInstanceState方法保存状态,此后恢复Activity,如果在这之间调用了onDestory,即Activity被意外销毁,会在onStart()和onResume()之间调用onRestoreInstanceState方法恢复状态,否则,会以onRestart->onStart()->onResume()的顺序重新打开Activity;
3)如果不配置Activity的configChanges或者配置没有起效果(注:如何起效果,视环境而定,最保险的方案目前是:android:configChanges="orientation|screenSize|keyboardHidden"),则在横竖屏切换的时候,会重新走一遍生命周期,否则,只会调用onConfigurationChanged方法;
补充一下关于configChanges的参数值解释:
Value | Description |
“mcc“ | The IMSI mobile country code (MCC) has changed — that is, a SIM hasbeen detected and updated the MCC.移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。 |
“mnc“ | The IMSI mobile network code (MNC) has changed — that is, a SIM hasbeen detected and updated the MNC.移动网号,在一个国家或者地区中,用于区分手机用户的服务商。 |
“locale“ | The locale has changed — for example, the user has selected a new language that text should be displayed in.用户所在地区发生变化。 |
“touchscreen“ | The touchscreen has changed. (This should never normally happen.) |
“keyboard“ | The keyboard type has changed — for example, the user has plugged in an external keyboard.键盘模式发生变化,例如:用户接入外部键盘输入。 |
“keyboardHidden“ | The keyboard accessibility has changed — for example, the user has slid the keyboard out to expose it.用户打开手机硬件键盘 |
“navigation“ | The navigation type has changed. (This should never normally happen.) |
“orientation“ | The screen orientation has changed — that is, the user has rotated the device.设备旋转,横向显示和竖向显示模式切换。 |
“fontScale“ | The font scaling factor has changed — that is, the user has selected a new global font size.全局字体大小缩放发生改变 |
根据这份解释,个人感觉其实系统是有BUG的,理论上configChanges=orientation这样的配置在设备旋转后就应该会去调用onConfigurationChanged方法,但实际上连亲生儿子也不遵循。
最后根据这次试验,总结一下Activity认为意外关闭的三种场景:
1)屏幕暗下去;
2)按下HOME键退出Activity;
3)横竖屏切换;
这三种情景都会导致Activity调用onSaveInstanceState去保存自己的状态以便于恢复。