Activity的生命周期和启动模式(一)

要点

在这里插入图片描述

一、Activity的生命周期

1、 生命周期情况分类
  • 正常情况下的生命周期(用户参与情况下经历的生命周期)
  • 异常情况下的生命周期(Acyivity被系统回收,或者设备的Configuration发生改变导致activity被销毁重建)
2、生命周期图

在这里插入图片描述
ps:图片来源网络

3、正常情况下的生命周期:

7种:

  • onCreate
  • onRestart
  • onStart
  • onResume
  • onPause
  • onStop
  • onDestroy

onCreate:生命周期的第一个方法,当我们打开一个activity时首先走这个方法。
我们可以做什么:初始化工作。比如加载界面(setContentView)、数据初始化(findviewbyid等等)

onStart :表示activity正在被启动。
特点:activity可见,未出现在前台。
理解:activity已经显示出来了,但是我们看不到。

onResume :表示activity已经可见,并且出现在前台,可以与用户进行交互.
例如:activity上有Button,此时我们就可以进行点击了。
与onStart的区别:
onStart 前台不可见,所以不可交互。 onResume 前台可见,所以可以交互。

onPause:表示activity正在停止,接着很快执行onStop
注意:极端情况下,跳转其他activity,并且快速的回到当前activity时,当前activity的生命周期:onPause->onResume
但是这个“快速回到”要很快,一般情况下都是onPause->onStop->onRestart->onStart->onResume
ps:这里不能做太耗时操作,可以做一些数据存储,动画停止 。

onStop:表示activity即将停止。

onRestart : 当前activity正在重新启动,一般是用户行为导致,比如用户切换Activity,或者摁home键回到桌面,当用户再次回到本activity时,当前activity 走 onRestart->onStart->onResume

onDestroy :activity即将被销毁 ,activity生命周期最后一个回调,这里可以做一些回收工作,资源释放。

4、正常情况下生命周期的切换过程

这里就以如下两个activity进行解读:
MainActivity:里面七个生命周期回调,和一个按钮。
Main2Activity:七个生命周期回调

代码如下(以这两个activity来说下切换流程)

MainActivity:

public class MainActivity extends AppCompatActivity {
    
    

    public static final String TAG = "tags";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
    
    
            @Override
            public void onClick(View v) {
    
    
                startActivity(new Intent(MainActivity.this, Main2Activity.class));
            }
        });
        Log.i(TAG, "onCreate:");
    }

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

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

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

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

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

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


Main2Activity 

public class Main2Activity extends AppCompatActivity {
    
    
    public static final String TAG = "tags";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
      //  setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
        setContentView(R.layout.activity_main2);
        Log.i(TAG, "Main2Activity onCreate:");
    }
    @Override
    protected void onStart() {
    
    
        super.onStart();
        Log.i(TAG, "Main2Activity onStart");
    }

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

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

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

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

(1) MainActivity 第一次启动时:
在这里插入图片描述

MainActivity 经历了:onCreate->onStart->onResume

(2)由mainactivity跳转到Main2Activity时:
在这里插入图片描述

MainActivity 经历了:onPause->onStop
ps:MainActivity走onPause时,新的Main2Activity会先走: Main2Activity onCreate -> Main2Activity onStart -> Main2Activity onResume 然后MainActivity 走 ->onStop
强调:
1、如果此时Main2Activity 已经设置了透明主题则mainActivity是不走onStop的,只走个onPause。从MainActivity跳转到Main2Activity 我们还是可以看见mainActivity,所以验证了onPause的可见不前台。
2、展示对话框不影响生命周期。

(3)在MainActivity时 摁Home键
在这里插入图片描述

MainActivity 走onPause->onStop

(4)摁Home后再次回到MainActivity时
在这里插入图片描述

此时MainActivity走了 onRestart->onStart->onResume

(5)启动mainActivity跳转到Main2Activity,再返回到 mainActivity
在这里插入图片描述

如上:
1对应启动mainActivity
2对应跳转到Main2Activity
3对应再返回到 mainActivity

(6)用户打开mainactivity在摁back键时

在这里插入图片描述

mainActivity走了 onPause->onStop->onDestroy

5、 生命周期分析

从整个生命周期来说onCreate和onDestroy 是配对的,分别代表着actiity的创建和销毁。并且只可能调用一次。
从activity是否可见来说onStart和onStop是配对的,随着用户的操作两个生命周期可能被多次执行。
从activity是否在前台来说onResume和onPause是配对的,随着用户的操作两个生命周期可能被多次执行。

6、 问题思考

问题1:onStart 和 onResume ,onPause 和onStop从描述上差不多对我们来说有什么实质不同?

从使用过程来讲,两对的确差不多,甚至我们可以只保留一对,比如只保留onStart ,onStop。既然如此为什么安卓系统还要提供看似重复的借口呢?根据上面的分析我们知道,这两对接口代表不同的意义。onStart 、onStop是从是否可见的角度来回调的(前者可见后者不可见)。
onResume 、onPause,是从是否前台的角度来回调的(前者前台,后者非前台)

问题2:假设当前activity为A,如果这时用户打开新的activityB,B的onResume 和A的onPause那个先执行

执行结果 A:onPause->B:onCreate->B:onStart->B:onResume->A:onStop
1:案例推导参考:上文4、(2)
2:activity的启动流程源码中找答案。(略,参看安卓开发艺术探索)

7、 异常情况下的生命周期
异常情况下的生命周期:
  • 资源相关的系统配置发生改变
  • 系统内存不足
7.1、资源相关配置发生改变导致Activity被杀死并重新创建

比如说当前activity处于竖屏状态,如果突然旋转屏幕由于系统配置发生改变,默认情况下activity就会被销毁重建。当然我们也可以阻止系统重新创建我们的activity。

栗子:默认情况下我们打开activity

在这里插入图片描述

这时旋转屏幕:
在这里插入图片描述

在这里插入图片描述

onSaveInstanceState、onRestoreInstanceState是我又添加的两个activity回调方法先不管,我们可以看出,activity首先销毁,然后重新建立。

7.1.1、异常情况下数据的存储:

其实activity还有两个方法onSaveInstanceState,onRestoreInstanceState。用来异常状态下存储数据。

  • onSaveInstanceState(Bundle outState)
    activity异常终止时系统会调用此方法来保存activity的状态,我们也可以在这里存储异常终止时我们要存的信息。(注意这个方法在activity正常终止是不会回调的)
  • onRestoreInstanceState(Bundle savedInstanceState)
    当activity异常终止重建时,系统会调用此方法,并把activity销毁时onSaveInstanceState所保存的Bundle 对象作为参数传递给onCreate和onRestoreInstanceState,因此我们可以通过onCreate和onRestoreInstanceState方法判断activity是否被重建。如果被重建了我们就可以取出之前保存的数据。

同时我们知道onSaveInstanceState、onRestoreInstanceState方法中系统还自动为我们做了一些恢复工作,当activity异常情况下重新创建时,系统默认为我们保存当前activity的视图结构。并在activity重新创建时为我们恢复这些工作。比如文本框中输入的数据,ListView滚动到的位置等,这些view相关的状态系统都会默认为我们存储,并恢复。但是哪些view系统能帮我们恢复哪些数据,就要看个view的源码了(和activity一样view有这两个方法回调)

有关保存和恢复view层次结构 采用了委托思想(参看安卓开发艺术)

异常情况下EditText 数据恢复举例:

写回调用于恢复我们自己想要存的数据

 @Override
    protected void onSaveInstanceState(Bundle outState) {
    
    
        super.onSaveInstanceState(outState);
        Log.i(TAG, "onSaveInstanceState: ");
        outState.putString("tom","异常下存数据 :tom");
    }

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

一个文本输入框(测试系统自动为我们回复的数据):
在这里插入图片描述

输入内容后让屏幕旋转:
在这里插入图片描述
在这里插入图片描述

可以看到回调走了数据也恢复了,editText内的数据系统帮我们恢复了,我们自定义的数据也恢复了
ps:特别注意我们的EditText一定要在xml布局中设置id,否则数据不会重新恢复(亲测,原因参看EditText源码)

我们在onSaveInstanceState中存数据,在onRestoreInstanceState和onCreate中都可以接受,接收区别如下:

  • onRestoreInstanceState
    一旦被调用其参数savedInstanceState一定是有值的,我们不用额外判空。
  • onCreate
    如果是正常启动其参数值为空,所以要额外判空。
7.2、资源内存不足导致低优先级的activity被杀死

这种情况我们不好模拟,但是其数据存储和恢复过程和资源配置发生改变回调是完全一致的

这里介绍下activity的优先级高低:

  • 前台activity
    正在和用户交互的activity,优先级最高。
  • 可见但非前台activity
    比如activity中弹出个对话框,导致activity还是可见,但是无法和用户交互了,优先级次于前台。
  • 后台activity
    已经被停止的activity,比如执行了onstop。优先级最低。

当系统内存不足时,系统会按照上述优先级杀死目标activity所在进程。并通过,onSaveInstanceState、onRestoreInstanceState来存储恢复数据。
如果一个进程中没有四大组件在执行(空进程),这个进程很快就会被系统杀死。因此一些后台工作,不适合脱离四大组件而独自运行在后台中,这样很容易被杀死。比较好的方法是将后台工作放入Service中从而保证进程有一定优先级,这样不会轻易被系统杀死。
ps:安卓中的进程(优先级从高到低):前台>可见>服务进程 >后台进程 >空进程

8、异常情况下禁止activity重新创建

上面介绍了异常情况下activity会重新创建,那么可以阻止activity重新创建吗?答案是有的给activity指定configChanges属性。(如下)

在这里插入图片描述

configChanges的属性值如下:
属性值 含义
mcc SIM卡唯一标识IMSI(国际移动用户识别码)中的国家代码,由三维数组组成,中国为460,此项标识mcc代码发生改变
mnc SIM卡唯一标识IMSI(国际移动用户识别码)中的运营商代码,由两位数字组成,中国移动TD系统为00,中国联通为01中国电信为03,此项标识mnc代码发生改变
locale 设备的本地位置发生改变,一般指切换了系统语言
touchscreen 触摸屏发生了变化,这个可以忽略,正常情况下不会发生
keyboard 键盘类型发生了变化 例如,用户插入了一个外置键盘。
keyboardHidden 键盘的可访问性发生改变比如用户调出键盘
navigation 系统导航方式发生改变,比如采用了轨迹球导航,很难发生,可以忽略
screenLayout 屏幕布局发生改变,很可能用户激活了另外一个显示设备
fontScale 系统字体缩放比例发生改变,比如用户选择了一个新字号
uiMode 用户界面模式发生改变,比如是否开启了夜间模式(api 8新添加)
orientation 屏幕方向发生改变,这个最常用,比如旋转了手机屏幕
screenSize 当屏幕的尺寸信息发生改变,当旋转设备屏幕时,屏幕尺寸会发生改变,这个选项比较特殊,他和编译选项有关,当编译选项中的minSdkVersion和targetSdkVersion均低于13时此选项不会导致activity重启,否则导致activity重启(api 13新添加)
smallestScreenSize 当设备的物理屏幕尺寸信息发生改变,这个属性值和屏幕的方向没关系,仅表示实际的物理屏幕发生改变时触发,比如用户切换到外设的显示设备,和screenSize一样,当编译选项中的minSdkVersion和targetSdkVersion均低于13时此选项不会导致activity重启,否则导致activity重启(api 13新添加)
layoutDirection 当布局方向发生改变,这个属性用的比较少正常情况下无法修改布局的layoutDirection(api 17新添加)

ps:上面的属性值我们可以配置多个 只需使用 | 连接即可。

从上面的表格我们发现如果我们没有为Activity指定configChanges的特定属性值,当异常情况下activity是会重新创建的。 上面表格中的属性值很多但是我们常用的也就几个-> locale、orientation、keyboardHidden。需要注意的是screenSize,smallestScreenSize只和编译版本有关,和运行环境无关。

栗子:

为activity添加onConfigurationChanged回调

 @Override
    public void onConfigurationChanged(Configuration newConfig) {
    
    
        super.onConfigurationChanged(newConfig);
        Log.i(TAG, "onConfigurationChanged: ");
    }

清单文件配置:
在这里插入图片描述
旋转屏幕:
在这里插入图片描述

我们发现activity旋转屏幕后并没有销毁重建而是走了onConfigurationChanged 方法。

小结

本节未完:本章只总结了activity的生命周期。activity的启动模式,intentFliter的匹配规则还没总结,剩余这两部分总结请参考:
Activity的生命周期和启动模式(二)

The end

读<安卓开发艺术探索>实践总结

猜你喜欢

转载自blog.csdn.net/qq_38350635/article/details/88855761