Activity的生命周期和启动模式

一. Activity的生命周期


Activity的生命周期分两种情况:

  1. 典型情况:在有用户参与的情况下,Activity所经过的生命周期的改变。
  2. 异常情况:Activity被系统回收或由于当前设备的Configuration发生改变从而导致Activity被销毁重建。

1. 典型情况下的生命周期分析

  1. 正常情况下,会经历如下生命周期:

    方法 功能 描述
    onCreate Activity正在被创建 可以做一些初始化工作,setContentView等
    onRestart Activity正在重新启动 Activity从不可见变为可见
    onStart Activity正在被启动,即将开始 Activity已经可见,但还没有出现在前台
    onResume Activity已开始活动 Activity已经可见,而且已出现在前台
    onPause Activity正在停止 可以做一存储数据、停止动画等工作,但要注意耗时
    onStop Activity即将停止 可以做一些稍微重量级的回收工作,同样要注意耗时
    onDestroy Activity正在被销毁建 可以做最后的回收工作和资源释放
  2. 生命周期的切换流程图:

    这里写图片描述

  3. 一些具体说明

    1. 第一次启动Activity: onCreate -> onStart -> onResume.
    2. 用户打开新的Activity或切换到桌面:onPause -> onStop
      • 如果新的Act采用了透明主题,那么当前Act将不会调用onStrop
    3. 当用户再次回到原Act时:onRestart -> onStart -> onResume
    4. 当用户按下back键回退时:onPause -> onStop -> onDestroy
    5. 当Act被系统回收后再次打开,生命周期回调过程同1一样,但会回调一个恢复数据的方法onRestoreInstanceState
    6. 回调方法的配对:

      周期方法1 周期方法2 Activity的状态动作 说明
      onCreate onDestroy 创建 - 销毁 只可能有一次调用
      onStart onStop 可见 - 不可见 用户操作与设备屏幕的点亮和熄灭,可能被调用多次
      onResume onPause 前台-后台 用户操作与设备屏幕的点亮和熄灭,可能被调用多次
  4. 从Act1 启动 Act2的过程:

    Act1.onPause -> Act2.onCreate -> Act2.onStart -> Act2.onResume -> Act1.onStop
    显然,旧Act的onPause首先调用,所以onPause里面不要进行耗时的操作。

2. 异常情况下的生命周期分析

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

    image

    • 系统会在onSaveInstanceStateonRestoreInstanceState方法中,系统自动做了一些恢复工作:保存当前Act的试图结构,并且在Act重启后恢复这些数据。
      • 我们可以覆盖这两个方法,来保存和恢复自定义的数据
      • 在这种异常情况下,onCreate的参数就是被保存的bundle
      • 在正常情况下,,onCreate的参数为null,而且onRestoreInstanceState不会被回调。
  2. 资源内存不足,导致低优先级的Activity被杀死

    • 当内存不足时,系统就会按照优先级高低去杀死目标Activity所在的进程,并后续通过onSaveInstanceStateonRestoreInstanceState方法来存储和恢复数据。
      • 如果一个进程没有四大组件在执行,那么这个进程将很快被系统杀死。
      • 所以将后台工作放入Service中从而保证进程有一定的优先级,不会被轻易地杀死。
    • 优先级说明:
      • 前台Activity:正在和用户交互的Activity,优先级最高
      • 可见但非前台Activity:比如弹出对话框,导致Activity可见但位于后台无法交互,优先级次之。
      • 后台Activity:已经被暂停的Activity,优先级最低。

二. Activity的启动模式


  1. LaunchMode:standard、singleTop、singleTask和singleInstance。
  2. Activity的Flags:
    • Intent.FLAG_ACTIVITY_NEW_TASK
    • Intent.FLAG_ACTIVITY_SINGLE_TOP
    • Intent.FLAG_ACTIVITY_CLEAR_TOP
    • Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS

1. LaunchMode

  1. standard: 标准模式,系统默认模式。

    • 如果用ApplicationContext启动Activity会报错,因为非Activity的Context没有任务栈。
    • 解决办法是指定Intent.FLAG_ACTIVITY_NEW_TASK标记,实际上是以singleTask模式启动的。
  2. singleTop:栈顶复用模式。

    • 当被启动的Act已处于栈顶,则不会重新创建一个Act,同时onNewIntent会被回调。
    • 当Act不处于栈顶,那么Act仍然会被重新创建。
  3. singleTask:栈内复用模式。这是一种单实例模式。

    • 只要Act在一个任务栈中,那么多次启动此Act都不会重新创建实例。
    • 过程:如果以SingleTask启动Act A,系统首先会寻找是否存在A想要的任务栈:
      1. 如果不存在,就创建一个任务栈,然后创建A的实例,再将A压入该栈。
      2. 如果存在,然后判断该栈中是否有A的存在。
        1. 如果不存在,则创建A的实例并压栈。
        2. 如果已存在,则将A调到栈顶,并回调onNewIntent方法。
    • 几个例子:
      • 已存在S1:ABC,以SingleTask启动D(S2)。
        • 先创建S2,再创建A的实例,并压入S2。
      • 已存在S1:ABC,以SingleTask启动D(S1)。
        • 创建A的实例,并压入S2。
      • 已存在S1:ADBC,以SingleTask启动D(S1)。
        • 直接将S1的D切换到栈顶,会触发回调onNewIntent
        • 由于栈的性质,BC被弹出
  4. singleIntance: 单实例模式。这是一种加强的singleTask模式。

    • 这种模式的Act,必须单独的位于一个任务栈中。
    • 当Act第一次启动时,系统会为它创建一个新的任务栈,然后A独自存在于这个任务栈中。
    • 当下一次启动Act时,则遵循栈内复用的特性,后续的启动请求均不会创建新的Act。
  5. TaskAffinity 与 allowTaskReparenting

    1. 示例1

      • 存在应用App1和应用App2:

        App Activity taskAffinity allowTaskReparenting
        App1 Act1(入口) com.test.affinity true
        App2 Act2(入口) com.test.affinity false
      • 操作过程:启动App1.Act1, 按下Home,在桌面启动App2
      • 结果:显示的仍然是App1.Act1,不是App2.Act2
    2. 示例2

      • 存在应用App1和应用App2

        App Activity taskAffinity
        App1 Act1 com.test.affinity
        App1 Act2(入口) 默认
        App2 Act3(入口) com.test.affinity

        Act2设置按钮,按下则启动Act1 (intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)).

      • 操作过程:启动App2, 按下home, 然后启动App1, 再按下back键 点击按钮启动Act1。

      • 结果:桌面显示顺序:Act3,桌面,Act2,Act1,Act3。
  6. 设置启动模式的方法:

    1. manifst中指定。无法设定FLAG_ACTIVITY_CLEART_TOP标识。
    2. 为intent设定Flag标志位. 无法指定SingleInstance模式。
      • 这两种方式同时存在时,以第二种为准

2. Activity的Flags,常用的几个如下:

  • Intent.FLAG_ACTIVITY_NEW_TASK
    • 说明:为Activity指定singleTask启动模式
  • Intent.FLAG_ACTIVITY_SINGLE_TOP
    • 说明:为Activity指定singleTop启动模式
  • Intent.FLAG_ACTIVITY_CLEAR_TOP
    • 说明:当具有此标志位的Activity启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。
      • 如果此Activity以singleTask模式启动,而且Activity的实例已经存在,那么系统就会调用它的onNewIntent。
      • 如果此此Activity以标准模式启动,那么除了位于它上面的Activity都要出栈,它自己也会出栈,系统会重新创建一个实例放入栈中。
  • Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    • 说明:具有此标记的Activity不会出现在历史Activity的列表中。它等同于android:excludeFromRecents="true"。当某些情况下,我们不希望用户通过历史列表回到我们的Activity时,这个标记比较有用。

三. IntentFilter的匹配规则

  • 只有action、category、data全部匹配,这个Intent才能成功启动Activity。
  • action匹配,Intent可以指定1个,或不指定
  • category匹配规则,Intent可以指定0个、1个或多个

- data匹配规则。

1. action匹配规则

  • Intent的action与IntentFilter的任和一个action匹配,那么匹配成功。
  • 如果Intent没有指定action,那么匹配失败。
  • action是字符串,所以匹配区分大小写。

2. category匹配规则

  • 如果Intent有1个或多个category,那么所有的category,都必须是过滤规则中已经定义了的category。这样才算匹配成功。
  • 如果Intent没有指定category,系统在调用startActivitystartActivityForResult时,会默认给Intent加上android.intent.category.DEFAULT这个category。
  • 为了使activity能够接收隐式调用,就必须在intent-filter中指定 android.intent.category.DEFAULT

3. data匹配规则

  1. data语法

    <data android:scheme="string"
          android:host="string"
          android:port="string"
          android:path="string"
          android:pathPattern="string"
          android:pathPrefix="string"
          android:mimeType="string" />
  2. data由两部分组成,mimeType和URI。

    • mimeType:媒体类型,比如image/jpeg、audio、mpeg4-generic等。
    • URI:
      • 结构:<scheme>://<host>:<port>/[<path>|<pathPrefix>|<pathPattern>]
      • 示例:content://com.example.project:200/folder/subfolder/etc, http://www.baidu.com:80/search/info
      • scheme: 模式,比如http、file、content等
      • Host:主机名。
      • Port:端口号,比如80。
      • path、pathPrefix和pathPattern:都表示路径信息。
        • path:完整的路径信息
        • pathPrefix:路径的前缀信息。
        • pathPattern:完整的路径信息,但可以包含通配符"*"。实际应用中应当加上转义符,比如 *要写成"\\*",\要写成"\\\\"
  3. 过滤匹配规则。

    • 规则:Intent必须含有data,而且data数据能够完全匹配过滤规则中的某一个data。
    • 示例1:

      • 存在过滤规则:

        <intent-filter>
            <data android:mimeType="image/* />
            ...
        </intent-filter>

        该规则指定了媒体类型为所有类型的图片,而且URI的scheme默认值为content和file。

      • Intent为了匹配以上规则,那么mimeType属性必须为"image/*",而且URI的scheme必须为content或file。例如:

        intent.setDataAndType(Uri.parse("file://abc"), "image/png");
      • 警告: 如果要为Intent指定完整的data,必须调用setDataAndType, 不能先调用setData,在调用setType,因为这两个方法会彼此清除对方的值。
    • 示例2:

      • 存在过滤规则:

        <intent-filter>
            <data android:mimeType="video/mpeg" android:scheme="http" ... /> 
            <data android:mimeType="audio/mpeg" android:scheme="http" ... />
                        ...
        </intent-filter>
      • 考虑intent设置data来匹配:

        intent.setDataAndType(Uri.parse("http://abc"), "video/mpeg");

        intent.setDataAndType(Uri.parse("http://abc"), "audio/mpeg");

        都可以匹配以上的规则。

  4. 当通过隐式方式启动Activity时,应先做判断,看是否存在Activity能够匹配我们的隐式Intent。

    • 使用PackageManagerresolveActivity,或者Intent的resolveActivity来查找匹配的Activity,失败则返回null。
    • 使用PackageManagerquerryIntentActivities, 可以返回所有成功匹配的Activity。

猜你喜欢

转载自blog.csdn.net/cangely/article/details/79851580