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

推荐阅读更多精彩内容