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

作者 : 开心源码 本文共6514个字,预计阅读时间需要17分钟 发布时间: 2022-05-12 共166人阅读

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

  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兔 会持续升级分享更多姿势哟~ (????) ~

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Activity的生命周期和启动模式——深度学习

发表回复