Activity的生命周期详述

正常情况下,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....


这说明在启动新的Activity之前,Stack顶部的Activity会首先被onPause(),新的Activity才会被启动,我们利用两个Activity打印的信息:

在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属性的内容及其含义:

属性内容含义
mccSIM卡唯一标识中的国家代号,由三位数字组成,中国是460,当这一项发生改变
mncSIM卡唯一标识中的运营商代号,由两位数字组成中国移动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);
}


参考资源链接:[Android Programming - The Big Nerd Ranch Guide (2nd Edition) (英文PDF版)](https://wenku.csdn.net/doc/64797e93543f8444881b70f6?utm_source=wenku_answer2doc_content) 掌握Activity生命周期对于开发稳定且效率高的Android应用至关重要。《Android Programming - The Big Nerd Ranch Guide (2nd Edition)》为你提供了全面的生命周期管理指导,帮助你避免常见的内存泄漏问题。首先,要在适当的生命周期阶段处理资源,例如在onStop()方法中释放资源以避免内存泄漏。其次,应该遵循Activity生命周期的顺序来安排对象的创建和销毁。在onDestroy()中清除资源并在onCreate()中重新创建它们,这样可以确保应用在各种生命周期状态转换中都能保持稳定。此外,理解生命周期回调间的依赖关系可以帮助开发者更合理地管理资源。例如,在onPause()方法中停止动画或视频播放可以避免在Activity不可见时消耗CPU资源。通过这种方式,你可以确保应用在运行时使用最少的资源,并在用户离开应用时正确释放资源。当内存紧张时,Android系统能够杀死处于后台的Activity,因此合理管理生命周期对于提高资源使用效率至关重要。在阅读《Android Programming - The Big Nerd Ranch Guide (2nd Edition)》后,你将深入了解如何运用这些技巧,从而优化你的Android应用性能和资源管理。 参考资源链接:[Android Programming - The Big Nerd Ranch Guide (2nd Edition) (英文PDF版)](https://wenku.csdn.net/doc/64797e93543f8444881b70f6?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值