手记

Activity的生命周期和启动模式——深度学习

一.典型情况下的生命周期

  1. onCreat: 表示Activity正在被创。再次方法中,我们可以做一些初始化工作,例如调用setContentView去加载界面布局资源、初始化Activity所需数据等。

  2. onRestart:表示Activity正在重新启动。一般情况下,当Activity从不可见变为可见状态时,此方法就会被调用。这种情况是由于用户操作导致,一般是由于用户点击的home键或点开了一个新的Activity,这时当前Activity就会暂停,就是onPause和onStop被执行了,接着用户又回到这个Activity,就会出现该情况。

  3. onStart:表示Activity正在被启动,即将开始。这时的Activity已经可见了,但没有出现在前台,无法和用户交互。

  4. onResume:表示Activity已经可见,并出现在前台开始活动。onStart和onResume都表示Activity已经可见,区别是,onStart的时候Activity在后台,而onResume的时候Activity位于前台。

  5. onPause:表示Activity正在停止。此时可以做一些数据储存,停止动画等操作,但注意不能太耗时,因为这样会影响新的Activity显示,因为onPause必须先执行完,新的Activity的onResume才会执行。正常情况下,紧接着onStop就会被调用,在特殊情况下,如果此时快速回到当前Activity,那么onRestart会被调用,这是一种极端情况,用户操作很难重现。

  6. onStop:表示Activity即将停止,可以做一些稍微重量级的工作,但同样不能太耗时。onStop和onPause的区别是,onPause的时候,Activity在前台,还能与用户交互,而onStop的时候,Activity就位于后台了。

  7. onDestory:表示Activity即将被销毁。我们可以在这个做一些回收工作和最终的资源释放。

    Activity生命周期切换过程.jpg

针对上图,附加说明几点:
(1)用户打开新的Activity或点击Home键时,回调如下:onPause -> onStop。这里有一特殊情况,若新的Activity采用了透明主题,则当前Activity不会调用onStop。

(2)当用户再次回到Activity时,回调如下:onRestart -> onStart -> onResume。

(3)当用户点击back键时,回调如下:onPause -> onStop -> onDestroy。

(4)从整个生命周期来说,onCreate和onDestroy配对,标识Activity的创建与销毁,且只能调用一次;从Activity是否可见来看,onStart和onStop是配对的,这两个方法会随用户操作而多次被调用;从Activity是否位于前台来说,onResume和onPause是配对的,这两个方法也会随用户操作而多次被调用。


二.异常情况下的生命周期

情况1:资源相关的系统配置发生改变导致Activity被杀死并重新创建

举例来说,当前Activity处于竖屏状态,若突然旋转屏幕,由于配置发生改变,默认情况下,Activity就会被销毁并重新创建。这时,如果我们不对Activity做特殊处理,那么Activity的生命周期就会如下图所示。


异常情况下Activity的重建过程.jpg

(1)当系统配置发生改变后,Activity会被销毁,其onPause、onStop、onDestroy会被调用,由于Activity是在异常情况下终止,系统会调用onSaveInstanceState来保存当前Activity的状态,此方法的调用时机是在onStop之前,它和onPause没有既定的时序关系。需要强调的是,这个方法在正常情况下系统不会调用。

(2)当Activity被重建时,系统会调用onRestoreInstanceState,并把Activity销毁时onSaveInstanceState方法所保存的bundle对象作为参数传递给onRestoreInstanceState和onCreate方法。因此,我们可以从onRestoreInstanceState和onCreate方法来判断Activity是否被重建。如果被重建,我们则可以取出之前保存的数据并恢复。从时序上看,onRestoreInstanceState是在onStart之后被调用。

(3)在onSaveInstanceState和onRestoreInstanceState方法中,系统会自动为我们做一定的恢复工作。例如,Activity在异常情况下重新创建时,系统会默认保存当前Activity的视图结构,并在Activity重启后恢复这些数据,比如文本框中输入的数据,ListView滚动位置等。具体对某一特定View系统能恢复那些数据,可以查看View的源码(和Activity一样,每个View都有onSaveInstanceState和onRestoreInstanceState方法)。

(4)关于保存和恢复View层次机构,系统工作流程如下:首先Activity意外终止,Activity会调用onSaveInstanceState去保存数据,然后Activity会委托Window来保存数据,接着Window会委托顶层容器去保存数据。顶层容器是个ViewGroup,一般来说很可能是个DecorView。最后,顶层容器再一一通知子元素来保存数据,这样整个数据保存过程就结束了。可以看出,这是一种典型的委托思想,上层委托下层,父容器委托子元素。这种思想在Android中有很多的应用,比如View的绘制过程、时间分发等等都采用了类似思想。

情况2:资源内存不足导致低优先级Activity被杀死

该情况我们不好模拟,但其储存模式和情况1完全一致。Activity的优先级从高到低,可分为如下三种:
(1)前台Activity——正在和用户交互的Activity,这种优先级最高。
(2)可见但非前台Activity——例如Activity弹出了一个对话框,导致Activity可见但位于后台无法和用户交互,比如执行了onPause,优先级次之。
(3)后台Activity——已经被暂停的Activity,比如执行了onStop,这种优先级最低。

当系统内存不足时,系统会按照上述优先级去杀死目标Activity所在进程,并后续通过onSaveInstanceState和onRestoreInstanceState方法来储存和恢复数据。若一个进程中没有四大组件在执行,那么这个进程将很快被杀死,较好的方法是将后台工作放入Service中,从而保证进程有一定优先级,这样就不会轻易被系统杀死了。

那么当系统配置发生改变后,什么方法才能让Activity不被重新创建呢?方法是这样的,我们可以给Activity指定configChanges属性。例如不想让Activity在屏幕旋转时重建,我们就可以给configChanges属性添加orientation这个值,如下。

android:configChanges="orientation"

若我们想指定多个值,可以使用“|”连接起来,如下。

android:configChanges="orientation|keyboardHidden"

系统配置中所含项目有很多,下面介绍了每个项目的含义。

  • “mcc“ 移动国家号码,由三位数字组成,每个国家都有自己独立的MCC,可以识别手机用户所属国家。

  • “mnc“ 移动网号,在一个国家或者地区中,用于区分手机用户的服务商。

  • “locale“ 所在地区发生变化。

  • “touchscreen“ 触摸屏已经改变。(这不应该常发生。)

  • “keyboard“ 键盘模式发生变化,例如:用户接入外部键盘输入。

  • “keyboardHidden“ 用户打开手机硬件键盘。

  • “navigation“ 导航型发生了变化。(这不应该常发生。)

  • “orientation“ 设备旋转,横向显示和竖向显示模式切换。

  • “fontScale“ 全局字体大小缩放发生改变。

  • “screenSize” 屏幕尺寸信息发生改变,和屏幕方向无关仅表示屏幕物理尺寸改变。当minSdkVersion一个小于等于13,为防止旋转屏幕时Activity重启,除了“orientation“,我们还要加上“screenSize”。

我们常用的时local,orientation,keyboardHidden这三个选项。


三.Activity的启动模式

为什么需要启动模式,因为在默认情况下,当我们多次启动同一Activity时,系统会创建多个实例并把它们一一入栈,而当我们点击back键时,这些Activity会一一回退。任务栈是一种“后进先出”的栈结构,每按一下back键就会有一个Activity出栈,直到栈空,系统会回收这个任务栈。上述是在Activity为默认启动模式时会出现的情况。

目前Activity有四种启动模式:standard、singleTop、singleTask、singleInstance。

1.Standard

标准模式。夜视系统的默认模式。每次启动一个Activity都会重新创建一个实例,无论该Activity是否已经存在。被创建的实例的onCreate,onStart,onResume都会被调用。如果Activity A启动Activity B(B为标准模式),则B就会进入A所在的任务栈中。

2.singleTop

栈顶复用模式。在此种模式下,如果新Activity已经位于任务栈栈顶,那么此Activity不会被重新创建,同时它的onNewIntent方法会被回调,通过此方法的参数我们能取出当前请求的信息。如果新Activity已经存在但不位于栈顶,那么新Activity仍然会被重新创建。

3.singleTask

栈内复用模式。这是一种单实例模式,在此模式下,只要Activity存在于一个栈中,那么多次启动这个Activity都不会被重新创建实例。下面我通过几个例子来更详细讲解singleTask的含义。(下面的Aty为Activity简称)

  • 若当前S1栈内的情况为ABC,这时Activity D以singleTask模式请求启动,它需要的任务栈为S2。由于S2与实例D均不存在,系统会先创建任务栈S2,再创建D的实例并入栈到S2内。(无栈,无Aty,先建栈,再建Aty并入栈)

  • 若前面的情况相同,这时这时Activity D需要的任务栈为S1。由于S1已存在。系统会创建D的实例并入栈到S1内。(有栈,无Aty,建Aty并入栈)

  • 若S1内仍未ABC,这时Activity B以singleTask模式请求启动,它需要的任务栈为S1。由于S1与实例B均存在,此时B不会被重建,系统会把B切换到栈顶位置并调用其onNewIntent方法,同时由于singleTask默认具有clearTop效果,会导致栈内B上面的Activity全部出战。于是最终S1内情况为BC。(有栈,有Aty,将A置顶且Aty以上所有Aty全部出栈)

  • singleTask启动模式中,Activity所需任务栈如何来制定呢?通过参数:TaskAffinity,可以翻译为任务相关性。这个参数可以标识Activity所需任务栈名字,默认情况下,所有Activity所需的任务栈名字为应用的包名。当然,我们也可以自己指定,指定的属性值必须不能与包名一样,否则相当于没指定。TaskAffinity主要和singleTask启动模式或allowTaskReparenting属性配对使用,在其他情况下无意义。
    ①TaskAffinity和singleTask配对使用,Activity会运行在TaskAffinity指定的任务栈中。
    ②TaskAffinity和allowTaskReparenting结合时,情况较复杂,有奇效。当A应用启动了B应用的某个Activity后,这时Activity会进入A应用任务栈中,若此Activity的allowTaskReparenting属性为true,那么当应用B被启动后,此Activity会直接从A应用任务栈转移至B应用的任务栈中。

4.singleInstance

单实例模式。这是一种加强的singleTask模式。它除了具有sigleTask所有特性外,还增加了一点,就是具有此种模式的Activity只能单独位于一个任务栈中。打个比方来说,若Activity A为singleInstance模式,当A启动,系统会为他创建一个新的任务栈并将A入栈,由于栈内复用特性,后续请求均不会创建A,除非这个独特的任务栈被系统销毁了。

指定启动模式的方法

1.第一种是在Android的Menifest通过launchMode属性来指定,如下:

<activity android:name=".SecondActivity"
      android:launchMode="singleTask"
      android:taskAffinity="MyActivity"/>

2.另一种是在Activity内通过在Intent设置标志位来指定,如下:

Intent intent = new Intent();
    intent.setClass(MainActivity.this,SecondActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);

3.这两种方式都可以指定Activity的启动模式,但二者有区别。其一,优先级上来讲,第二种方法要高于第一种;其二,限定范围上,第一种方法无法直接设定FLAG_ACTIVITY_CLEAR_TOP标识,而第二种方法无法指定singleInstance模式。


四.Activity的Flags

Activity的Flags有很多,我们主要分析一些较常用的标记位。
标记位的做有很多,有的标记为可以设定Activity的启动模式,比如FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_SINGLE_TOP等;还有的标记位可以影响Activity的运行状态,比如FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS等。
大部分情况下,我们不需要为Activity设置标记位,在使用标记位时,要注意有的标记位是系统内部使用的,应用程序不需要手动设置这些标记位以防出现问题。

  • FLAG_ACTIVITY_NEW_TASK
    此标记位作用是为Activity指定singleTask启动模式,其效果合在XML中指定该启动模式相同。

  • FLAG_ACTIVITY_SINGLE_TOP
    此标记位作用是为Activity指定singleInstance启动模式,其效果合在XML中指定该启动模式相同。

  • FLAG_ACTIVITY_CLEAR_TOP
    具有该标记位的Activity启动时,若启动的Activity为singleTask启动模式,则同一任务栈内它之上的Activity都会出栈;若启动的Activity为standard启动模式,则同一任务栈内连同它之上的Activity都会出栈,系统会重建一个新的Activity入栈。

  • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    设置该标记位的Activity不会出现在历史Activity列表,当某些情况下我们不希望用户通过历史列表回到我们这个Activity时较为有用。它等同于在XML中指定属性android:excludeFromRecents="true"。




作者:TuTu兔
链接:https://www.jianshu.com/p/d3840a1fa57a


0人推荐
随时随地看视频
慕课网APP