Activity生命周期

Android官方文档和其他不少资料都对Activity生命周期进行了详细介绍,结合资料本文将对Activity生命周期进行一次总结。
Activity是由Activity栈进管理,当来到一个新的Activity后,此Activity将被加入到Activity栈顶,之前的Activity位于此Activity底部。Acitivity一般意义上有四种状态:
1.当Activity位于栈顶时,此时正好处于屏幕最前方,此时处于运行状态;
2.当Activity失去了焦点但仍然对用户可见(如栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕),此时处于暂停状态;
3.当Activity被其他Activity完全遮挡,此时此Activity对用户不可见,此时处于停止状态;
4.当Activity由于人为或系统原因(如低内存等)被销毁,此时处于销毁状态;
在每个不同的状态阶段,Adnroid系统对Activity内相应的方法进行了回调。因此,我们在程序中写Activity时,一般都是继承Activity类并重写相应的回调方法。
我们来看一下这一张经典的生命周期流程图:
先贴一张来自官方文档(http://developer.android.com/reference/android/app/Activity.html
)的图,相信大家都看到过。

Activity生命周期.png

<pre><code>
public class Activity extends ApplicationContext {

 protected void onCreate(Bundle savedInstanceState);

 protected void onStart();

 protected void onRestart();

 protected void onResume();

 protected void onPause();

 protected void onStop();

 protected void onDestroy();

}</pre></code>
相信不少朋友也已经看过这个流程图了,也基本了解了Activity生命周期的几个过程,我们就来说一说这几个过程。
(结论都是在Android6.0系统上亲测)
1、当一个Activity正常启动时:onCreate->onStart->onResume-> onWindowFocusChanged,Activity进入运行状态。
<pre><code>
09-18 10:57:01.264 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onCreate called.

09-18 10:57:01.274 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStart called.

09-18 10:57:01.274 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onResume called.

09-18 10:57:01.314 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.</pre></code>
2、当前Activity被其他Activity完全覆盖其上:系统会调用onPause->onWindowFocusChanged->onSaveInstanceState->onStop方法,暂停当前Activity的执行。
<pre><code>
09-18 11:01:18.654 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

09-18 11:01:18.664 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.

09-18 11:01:19.014 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onSaveInstanceState called. put param: 1

09-18 11:01:19.014 9686-9686/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStop called.
</pre></code>
3、当前Activity被对话框遮挡失去焦点(没有完全遮挡),系统会调用onWindowFocusChanged(没有调用其他声生命周期方法)
<pre><code>
09-18 11:07:01.094 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.
</pre></code>
4、修改OrientationActivityAndroidManifest.xml中的配置,将android:theme属性设置为@android:style/Theme.Dialog,然后再点击LifeCycleActivity中的按钮,跳转行为就变为了OrientationActivity覆盖到LifeCycleActivity之上了,(和第3点有区别)此时调用的方法为:onPause->onWindowFocusChanged->onSaveInstanceState
<pre><code>
01-19 00:00:15.170 17422-17422/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

01-19 00:00:15.180 17422-17422/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.

01-19 00:00:15.510 17422-17422/xiaoyong68.com.lifecycle I/LifeCycleActivity: onSaveInstanceState called. put param: 1
</pre></code>
5、当前Activity正处于运行状态时按home键回到后台:onWindowFocusChanged->onPause->onSaveInstanceState->onStop
<pre><code>
09-18 11:25:02.089 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.

09-18 11:25:02.089 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

09-18 11:25:02.099 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onSaveInstanceState called. put param: 1

09-18 11:25:02.099 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStop called.

</pre></code>
6、当前Activity正处于运行状态时锁屏:onPause->onSaveInstanceState->onStop->onWindowFocusChanged
<pre><code>
09-18 11:27:45.199 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

09-18 11:27:45.229 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onSaveInstanceState called. put param: 1

09-18 11:27:45.229 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStop called.

09-18 11:27:46.149 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.</pre></code>
7、当解锁屏幕Activity处于完全可见状态(或者从后台、从另外一个Activity跳转回到Activity):onRestart->onStart->onResume->onWindowFocusChanged
<pre><code>09-18 11:36:37.739 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onRestart called.

09-18 11:36:37.739 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStart called.

09-18 11:36:37.739 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onResume called.

09-18 11:36:37.849 13787-13787/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.</pre></code>
8、(1)用户进行横竖屏切换,当没有在AndroidManifest.xml中添加android:configChanges="orientation|screenSize"这句,将不会调用onConfigurationChanged方法:onPause->onSaveInstanceState->onStop->onDestory->onCreate->onStart->onRestoreInstanceState->onResume->onWindowFocusChanged
<pre><code>
01-18 22:07:58.290 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

01-18 22:07:58.290 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onSaveInstanceState called. put param: 1

01-18 22:07:58.290 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStop called.

01-18 22:07:58.290 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onDestory called.

01-18 22:07:58.340 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onCreate called.
01-18 22:07:58.360 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStart called.

01-18 22:07:58.360 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onRestoreInstanceState called. get param: 1

01-18 22:07:58.360 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onResume called.

01-18 22:07:58.400 19381-19381/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.
</pre></code>
(2)如果在AndroidManifest.xml中添加android:configChanges="orientation|screenSize"这句,只会调用onConfigurationChanged方法,不会调用其他生命周期方法:onConfigurationChanged
<pre><code>
01-18 22:18:56.300 29640-29640/xiaoyong68.com.lifecycle I/OrientationActivity: onConfigurationChanged called.

</pre></code>
8当Activity按back键正常退出:onPause->onWindowFocusChanged->onStop->onDestory
<pre><code>
01-18 23:22:06.120 29640-29640/xiaoyong68.com.lifecycle I/LifeCycleActivity: onPause called.

01-18 23:22:06.140 29640-29640/xiaoyong68.com.lifecycle I/LifeCycleActivity: onWindowFocusChanged called.

01-18 23:22:06.140 29640-29640/xiaoyong68.com.lifecycle I/LifeCycleActivity: onStop called.

01-18 23:22:06.140 29640-29640/xiaoyong68.com.lifecycle I/LifeCycleActivity: onDestory called.

</pre></code>

下面我们就结合实例,来演示一下生命周期的几个过程的详细情况。我们新建一个名为lifecycle的项目,创建一个名为LifeCycleActivity的Activity,如下:
<pre><code>
package xiaoyong68.com.lifecycle;

import android.app.Activity;

import android.content.Context;

import android.content.DialogInterface;

import android.content.Intent;

import android.support.v7.app.AlertDialog;

import android.os.Bundle;

import android.util.Log;

import android.view.View;

import android.widget.Button;

/**

  • Activity生命周期测试
    */

public class LifeCycleActivity extends Activity {

private static final String TAG = "LifeCycleActivity";
private Context context = this;
private int param = 1;

//Activity创建时被调用
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.i(TAG, "onCreate called.");

    setContentView(R.layout.activity_lifecycle);

    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(context, OrientationActivity.class);
            startActivity(intent);

// showDialog();

        }
    });
}


private void showDialog(){
    /* @setIcon 设置对话框图标
     * @setTitle 设置对话框标题
     * @setMessage 设置对话框消息提示
     * setXXX方法返回Dialog对象,因此可以链式设置属性
     */
    final AlertDialog.Builder normalDialog =
            new AlertDialog.Builder(LifeCycleActivity.this);
    normalDialog.setTitle("我是一个普通Dialog");
    normalDialog.setMessage("你要点击哪一个按钮呢?");
    normalDialog.setPositiveButton("确定",
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //...To-do
                }
            });
    normalDialog.setNegativeButton("关闭",
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //...To-do
                }
            });
    // 显示
    normalDialog.show();
}

//Activity创建或者从后台重新回到前台时被调用
@Override
protected void onStart() {
    super.onStart();
    Log.i(TAG, "onStart called.");
}

//Activity从后台重新回到前台时被调用
@Override
protected void onRestart() {
    super.onRestart();
    Log.i(TAG, "onRestart called.");
}

//Activity创建或者从被覆盖、后台重新回到前台时被调用
@Override
protected void onResume() {
    super.onResume();
    Log.i(TAG, "onResume called.");
}

//Activity窗口获得或失去焦点时被调用,在onResume之后或onPause之后
@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    Log.i(TAG, "onWindowFocusChanged called.");
}

//Activity被覆盖到下面或者锁屏时被调用
@Override
protected void onPause() {
    super.onPause();
    Log.i(TAG, "onPause called.");
    //有可能在执行完onPause或onStop后,系统资源紧张将Activity杀死,所以有必要在此保存持久数据
}

//退出当前Activity或者跳转到新Activity时被调用
@Override
protected void onStop() {
    super.onStop();
    Log.i(TAG, "onStop called.");
}

//退出当前Activity时被调用,调用之后Activity就结束了
@Override
protected void onDestroy() {
    super.onDestroy();
    Log.i(TAG, "onDestory called.");
}

/**
 * Activity被系统杀死时被调用.
 * 例如:屏幕方向改变时,Activity被销毁再重建;当前Activity处于后台,系统资源紧张将其杀死.
 * 另外,当跳转到其他Activity或者按Home键回到主屏时该方法也会被调用,系统是为了保存当前View组件的状态.
 * 在onPause之前被调用.
 */
@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("param", param);
    Log.i(TAG, "onSaveInstanceState called. put param: " + param);
    super.onSaveInstanceState(outState);
}

/**
 * Activity被系统杀死后再重建时被调用.
 * 例如:屏幕方向改变时,Activity被销毁再重建;当前Activity处于后台,系统资源紧张将其杀死,用户又启动该Activity.
 * 这两种情况下onRestoreInstanceState都会被调用,在onStart之后.
 */
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    param = savedInstanceState.getInt("param");
    Log.i(TAG, "onRestoreInstanceState called. get param: " + param);
    super.onRestoreInstanceState(savedInstanceState);
}

}
</code></pre>
大家注意到,除了几个常见的方法外,我们还添加了onWindowFocusChangedonSaveInstanceStateonRestoreInstanceState方法:
1.onWindowFocusChanged方法:在Activity窗口获得或失去焦点时被调用,例如创建时首次呈现在用户面前;当前Activity被其他Activity覆盖;当前Activity转到其他Activity或按Home键回到主屏,自身退居后台;用户退出当前Activity。以上几种情况都会调用onWindowFocusChanged,并且当Activity被创建时是在onResume之后被调用,当Activity被覆盖或者退居后台或者当前Activity退出时,它是在onPause之后被调用,如图所示:

这个方法在某种场合下还是很有用的,例如程序启动时想要获取视特定视图组件的尺寸大小,在onCreate中可能无法取到,因为窗口Window对象还没创建完成,这个时候我们就需要在onWindowFocusChanged里获取;
2.onSaveInstanceState:(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,此方法会被调用;(2)在用户改变屏幕方向时,此方法会被调用;(3)在当前Activity跳转到其他Activity或者按Home键回到主屏,自身退居后台时,此方法会被调用。第一种情况我们无法保证什么时候发生,系统根据资源紧张程度去调度;第二种是屏幕翻转方向时,系统先销毁当前的Activity,然后再重建一个新的,调用此方法时,我们可以保存一些临时数据;第三种情况系统调用此方法是为了保存当前窗口各个View组件的状态。onSaveInstanceState的调用顺序是在onPause之前。
3.onRestoreInstanceState
(1)在Activity被覆盖或退居后台之后,系统资源不足将其杀死,然后用户又回到了此Activity,此方法会被调用;
(2)在用户改变屏幕方向时,重建的过程中,此方法会被调用。我们可以重写此方法,以便可以恢复一些临时数据。onRestoreInstanceState的调用顺序是在onStart之后。
后面还会有一篇文章专门描述这3个方法应用场景。

接下来介绍一下关于Activity屏幕方向的相关知识。
我们可以为一个Activity指定一个特定的方向,指定之后即使转动屏幕方向,显示方向也不会跟着改变:
1.指定为竖屏:在AndroidManifest.xml中对指定的Activity设置android:screenOrientation="portrait",或者在onCreate方法中指定:
<pre><code>
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //竖屏
</pre></code>
2.指定为横屏:在AndroidManifest.xml中对指定的Activity设置android:screenOrientation="landscape",或者在onCreate方法中指定:
<pre><code>
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); //横屏 </pre></code>
为应用中的Activity设置特定的方向是经常用到的办法,可以为我们省去不少不必要的麻烦。不过,我们今天讲的是屏幕方向改变时的生命周期,所以我们并不采用固定屏幕方向这种办法。
下面我们就结合实例讲解一下屏幕转换的生命周期,我们新建一个Activity命名为OrientationActivity,如下:
<pre><code>
package xiaoyong68.com.lifecycle;

import android.app.Activity;

import android.content.res.Configuration;

import android.os.Bundle;

import android.util.Log;

/**

  • Activity生命周期测试
    */

public class OrientationActivity extends Activity {

private static final String TAG = "OrientationActivity";
private int param = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_orientation);
    Log.i(TAG, "onCreate called.");
}

@Override
protected void onStart() {
    super.onStart();
    Log.i(TAG, "onStart called.");
}

@Override
protected void onRestart() {
    super.onRestart();
    Log.i(TAG, "onRestart called.");
}

@Override
protected void onResume() {
    super.onResume();
    Log.i(TAG, "onResume called.");
}

@Override
protected void onPause() {
    super.onPause();
    Log.i(TAG, "onPause called.");
}

@Override
protected void onStop() {
    super.onStop();
    Log.i(TAG, "onStop called.");
}

@Override
protected void onDestroy() {
    super.onDestroy();
    Log.i(TAG, "onDestory called.");
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt("param", param);
    Log.i(TAG, "onSaveInstanceState called. put param: " + param);
    super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    param = savedInstanceState.getInt("param");
    Log.i(TAG, "onRestoreInstanceState called. get param: " + param);
    super.onRestoreInstanceState(savedInstanceState);
}

//当指定了android:configChanges="orientation|screenSize"后,方向改变时onConfigurationChanged被调用
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    Log.i(TAG, "onConfigurationChanged called.");
    switch (newConfig.orientation) {
        case Configuration.ORIENTATION_PORTRAIT:

// setContentView(R.layout.orientation_portrait);

            Log.i(TAG, "1");
            break;
        case Configuration.ORIENTATION_LANDSCAPE:
            Log.i(TAG, "2");

            break;

    }

}

}

</pre></code>

Activity的生命周期与程序的健壮性有着密不可分的关系,希望朋友们能够认真体会、熟练应用。

小插曲:在调试的生活碰到一个奇怪的问题:
onConfigurationChanged未被调用???
根据正常认知,在AndroidManifest.xml中设置Android:configChanges="orientation“,然后在Java代码中重写onConfigurationChanged,即不会重复Activity生命周期方法,而是调用onConfigurationChanged
然而今天这种做法没有效果,什么原因呢。
原来,自从Android 3.2(API 13),screen size也开始跟着设备的横竖切换而改变。
所以,在AndroidManifest.xml里设置的MiniSdkVersionTargetSdkVersion属性大于等于13的情况下,
如果你想阻止程序在运行时重新加载Activity,除了设置”orientation“,你还必须设置"ScreenSize"
解决方法:AndroidManifest.xml中设置android:configChanges="orientation|screenSize“
4.2又碰到同样的问题了,还得再加上个"layoutDirection"

备注:
Activity的onSaveInstanceState()onRestoreInstanceState()并不是生命周期方法,它们不同于onCreate()onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity时,onSaveInstanceState()才会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。因为在这种情况下,用户的行为决定了不需要保存Activity的状态。通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
另外,当屏幕的方向发生了改变,Activity会被摧毁并且被重新创建,如果你想在Activity被摧毁前缓存一些数据,并且在Activity被重新创建后恢复缓存的数据。可以重写Activity的onSaveInstanceState()onRestoreInstanceState()方法,如下:
<pre><code>
public class PreferencesActivity extends Activity {

private String name;

protected void
onRestoreInstanceState(BundlesavedInstanceState) {

name = savedInstanceState.getString("name");//被重新创建后恢复缓存的数据

super.onRestoreInstanceState(savedInstanceState);

}

protected void onSaveInstanceState(BundleoutState) {

outState.putString("name", "liming");//被摧毁前缓存一些数据

super.onSaveInstanceState(outState);

}

}
</pre></code>

demo下载地址:https://github.com/xiaoyong68/blog_TestCode_save.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容