正常情况下,Activity会经历如下的生命周期:
onCreate(), onStart(), onResume(), onPause(), onStop(), onDestroy()
onPause():当当前Activity被半遮挡,比如当前Activity上弹出了一个弹窗,则会调用该Activity的onPause()方法;
onStop(): 当前Activity被完全遮盖的时候,就会调用该方法,比如当当前Activity中开启一个Activity,被完全遮盖,Activity会依次调用onPause(),onStop()方法
正常的生命周期需要注意:
(1)针对一个特定的Activity,第一次启动,回调:onCreate() -> onStart() -> onResume();
(2)当用户打开一个新的Activity或者切换到桌面,回调:onPause() -> onStop(),不过当弹出的Activity采用了透明主题,或者弹出的是一个Dialog,原来的Activity没有被完全遮盖,那么原来Activity只会调用onPause()方法;
(3)当用户再次回到原来的Activity时,回调:onRestart() -> onStart() -> onResume();
(4)当用户按下BACK键时,Activity调用: onPause() -> onStop() -> onDestroy();
注意的一个问题:
ActivityA为当前Activity,用户打开了一个新的ActivityB,那么ActivityB的onResume()和ActivityA的onPause()哪个先执行呢?
ActivityManagerService会维持一个ActivityStack,负责栈内Activity的维护,其中ActivityStack中有一段源码的注释:
// we need to start pausing the current activity
// so the top one can be resumed....
在FirstActivity中打开SecondActivity:
FirstActivity -> onPause()
SecondActivity -> onCreate()
SecondActivity -> onStart()
SecondActivity -> onResume()
FirstActivity -> onStop()
异常情况下Activity的生命周期:
情景一:系统配置发生改变导致Activity被Destroy后重新Create
情景二:系统资源内存不足,导致优先级低的Activity被回收
当Activity是竖屏,且没有对其进行任何设置,那么默认的当Activity被横屏之后,Activity会首先被Destroy,然后重新创建:
Activity: onSaveInstanceState() -> onDestroy() -->> onCreate() -> onRestoreInstanceState()
当系统配置发生改变的时候,Activity会被销毁,会调用Activity的onPause(), onStop() , onDestroy()方法,同时异常情况下,Activity在销毁之前,会调用Activity的onSaveInstanceState()方法,该方法的调用时机在onStop()之前,但是和onPause()方法没有明确的时序关系,需要注意的是,onSaveInstanceState()方法只会在Activity异常销毁的时候被调用,在Activity正常销毁的情况下是不会调用此方法的。异常情况下,Activity再次被创建时,会调用onRestoreInstanceState()方法,在此方法中会传递一个Bundle对象,这个Bundle对象中保存了Activity异常销毁时调用onSaveInstanceState()的时候保存在Bundle中的所有参数,而onSaveInstanceState()中保存的Bundle会同时传递给onCreate()和onRestoreInstanceState()两个方法,onRestoreInstanceState()方法的调用时机在onStart()之后。
开发者可以利用onSaveInstanceState(),onRestoreInstanceState()和onCreate()方法来恢复一些必要的数据,系统同时也利用onSaveInstanceState()和onRestoreInstanceState()方法恢复一些数据,比如TextView中的内容,ListView滚动的位置等,可以查看对应的View的源码,来查看该View会恢复哪些数据,以TextView为例:
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
// Save state if we are forced to
boolean save = mFreezesText;
int start = 0;
int end = 0;
if (mText != null) {
start = getSelectionStart();
end = getSelectionEnd();
if (start >= 0 || end >= 0) {
// Or save state if there is a selection
save = true;
}
}
if (save) {
SavedState ss = new SavedState(superState);
// XXX Should also save the current scroll position!
ss.selStart = start;
ss.selEnd = end;
if (mText instanceof Spanned) {
/*
* Calling setText() strips off any ChangeWatchers;
* strip them now to avoid leaking references.
* But do it to a copy so that if there are any
* further changes to the text of this view, it
* won't get into an inconsistent state.
*/
Spannable sp = new SpannableString(mText);
for (ChangeWatcher cw : sp.getSpans(0, sp.length(), ChangeWatcher.class)) {
sp.removeSpan(cw);
}
SuggestionSpan[] suggestionSpans = sp.getSpans(0, sp.length(), SuggestionSpan.class);
for (int i = 0; i < suggestionSpans.length; i++) {
int flags = suggestionSpans[i].getFlags();
if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
&& (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
sp.removeSpan(suggestionSpans[i]);
}
}
sp.removeSpan(mSuggestionRangeSpan);
ss.text = sp;
} else {
ss.text = mText.toString();
}
if (isFocused() && start >= 0 && end >= 0) {
ss.frozenWithFocus = true;
}
ss.error = mError;
return ss;
}
return superState;
}
从上面的源码可以看到,TextView保存了输入内容,利用Spannable以富文本方式保存。
Activity被意外回收的生命周期:
onPause() -> onSaveInstanceState() -> onStop() -> onDestroy()
我们利用onSaveInstanceState()存储数据:
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString("account", "mxd_hust");
}
在Activity被重新创建时,可以在onCreate()和onRestoreInstanceState()中获得在onSaveInstanceState()中存储的数据:
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null)
{
String account = savedInstanceState.getString("account", "--");
}
}
或者从onRestoreInstanceState()中获取:
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
super.onRestoreInstanceState(savedInstanceState);
String account = savedInstanceState.getString("account", "--");
}
我们知道,当系统配置发生改变的时候,Activity会被重新创建,通过为Activity配置configChanges属性,可以避免Activity的重构,configChanges属性内容很多,为了让某一项内容改变的时候Activity不发生重构,那么可以通过为该Activity的configChanges添加这一项属性内容,比如我们不想让Activity在屏幕横竖屏切换时发生重构,则设置:
android:configChanges="orientation"
多个属性内容使用|分割,比如:
android:configChanges="orientation|keyboardHidden"
下表为Activity的configChanges属性的内容及其含义:
属性内容 | 含义 |
mcc | SIM卡唯一标识中的国家代号,由三位数字组成,中国是460,当这一项发生改变 |
mnc | SIM卡唯一标识中的运营商代号,由两位数字组成中国移动TD系统为00,中国联通为01,中国电信为03,此项发生改变 |
locale | 设备的本地位置发生了改变,一般指系统语言发生改变 |
keyboard | 键盘类型发生改变,比如用户使用了外接键盘 |
keyboardHidden | 键盘的可访问性发生了改变,比如用户调出了键盘 |
screenLayout | 屏幕布局发生了改变,比如用户外接了一个显示器 |
uiMode | 用户界面模式发生了改变,比如变为了夜间模式(API8新添加的夜间模式) |
orientation | 屏幕横竖屏的切换 |
screenSize | 当屏幕尺寸发生改变,当旋转设备屏幕时,屏幕尺寸也会发生改变,这个属性内容和编译环境有关, 当编译的minSdkVersion和targetSdkVersion低于13时此属性内容不会导致Activity重建,否则会导致Activity重建(API13中新添加) |
smallestScreenSize | 设备屏幕的物理尺寸发生变化,比如当用户切换成外部显示器,同样的smallestScreenSize是在API13新添加的内容, 在minSdkVersion和targetSdkVersion大于13时,才会导致Activity重构 |
layoutDirection | 当Activity的布局发生改变,用的很少 |
在AndroidManifest.xml文件中设置configChanges属性:
<activity
android:name=".MainActivity"
android:configChanges="orientation|keyboard"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
此时,设置了Activity的configChanges属性之后,Activity就不会再被重构,onSaveInstanceState()和onRestoreInstanceState()方法也不会被调用,取而代之的是调用onConfigurationChanged()方法:
@Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
Log.v(LOG, "MainActivity: " + newConfig.orientation);
}