0%

Android 笔记:Activity 二三事

本文主要记录一些与 Activity 相关的,并不是很容易弄清楚的点。

Activity 生命周期

先上 Google 官方的图:

Activity 生命周期
  • onCreate():当前 Activity 实例第一次被创建时调用
  • onStart():Activity 不可见到可见 时调用
  • onResume(): Activity 获取焦点,可以进行交互时调用
  • onPause():Activity 失去焦点,无法交互时调用
  • onStop():Activity 完全不可见时调用
  • onDestroy():Activity 即将销毁前调用
  • onRestart():Activity 正在被重新启动时调用

这里又引申出 Activity 相关的三个生存期:

  • 完整生存期: onCreate() -> onDestroy()
  • 可见生存期: onStart() -> onStop()
  • 前台(可交互)生存期: onResume() -> onPause()
     
  • Q:Activity A 启动 Activity B 时,A 的 onPause() 先执行还是 B 的 onResume() 先执行呢?
    A:A 的 onPause() 先执行。
    结论:考虑到只有在当前 Activity 的 onPause() 执行完毕之后,新的 Activity 的 onResume() 才执行,所以,onPause() 中尽量别做重量级操作

异常情况下的生命周期

Activity 异常情况重建过程

这里的 异常情况 主要指以下两种情况:

  1. 系统相关的资源配置发生改变产生的异常:
    如果Activity不作特殊处理,当系统配置发生改变(比如屏幕方向发生改变),Activity就会被销毁并重建,onSaveInstanceState()onRestoreInstanceState() 这两个方法就会在这一过程中被调用。

  2. 内存资源不足导致 Activity 被回收的异常:
    Activity会有优先级分级情况,优先级依次降低:

    • 前台 Acitivty,即正在交互的 Activity
    • 可见但非前台 Activity
    • 不可见后台 Activity

    内存资源不足导致 Activity 被回收,在后续也会通过 onSaveInstanceState()onRestoreInstanceState() 存储和恢复数据。
    没有四大组件在运行的进程很容易被系统杀死,所以,一些后台工作不适合脱离四大组件独自后台运行,最佳实践是依赖 Service 以保证进程有较高的优先级。

Activity 在异常情况下进行重建时,系统会默认对 View 相关的状态进行存储和恢复,具体会恢复特定 View 的什么状态数据需要去查阅源码。也就是说,非 View 相关的数据,需要自行存储和恢复。

针对上面的第一种情况,ActivityconfigChanges 可以指定一些资源配置属性,当这些资源配置发生改变之后,Activity 不会重建,不会调用 onSaveInstanceState()onRestoreInstanceState(),取而代之的是回调 onConfigurationChanged() 方法。最常用的 configChanges 选项有:

  • locale:设备的本地位置发生改变,一般指切换系统语言;
  • orientation:屏幕方向发生改变,比如旋转手机屏幕;
  • keyboardHidden:键盘的可访问性发生改变,比如用户调出键盘。

Activity 启动模式

ActivitylaunchMode 由四种:

  1. standard:默认启动方式,每次启动这样的 Activity 都会生成创建新实例。谁启动以 standardlaunchModeActivity, 该 Activity 就会运行在它的那个任务栈中

  2. singleTop:栈顶复用模式,要启动的 Activity 已经有实例在栈顶,则不会生成新实例,不会调用 onCreate()onStart() 而会调用该 ActivityonNewIntent() 方法;其他情况,参考 standard 模式;

  3. singleTask:栈内复用模式。比如 Activity A 的 TaskAffinity 属性指定的任务栈为 a,当启动 A 时,如果任务栈 a 不存在,则创建 a 任务栈,并创建 Activity A 让其入栈;如果任务栈 a 存在,且 A 实例不存在,则 A 入栈;如果任务栈 a 存在,且 A 实例存在,则清空(pop)在 A 之上的所有 Activity 让 A 置于栈顶,并回调 A 的 onNewIntent() 方法;

    TaskAffinity 即 任务相关性,这个参数标识 Activity 所需要的任务栈的名字,默认情况下,这个值是应用包名。 TaskAffinity 主要和 singleTask 启动模式配对使用,singleTask 启动模式的 Activity 会启动在 TaskAffinity 指定的任务栈中。比如:

    1
    2
    3
    4
    <activity
    android:name="com.example.ExampleActivity"
    android:taskAffinity="com.exmple.exampleTask"
    android:launchMode="singleTask"/>
  4. singleInstance:单实例模式。可以看做是特殊的 singleTask 模式,除了具有 singleTask 特性外,此种模式的 Activity 只能单独在一个任务栈。

IntentFilter 匹配规则

IntentFilter 用在 Activity 的隐式调用上,先看 IntentFilter 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<activity android:name="com.example.ExampleActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="com.example.intent.category.CUSTOM_1" />
<category android:name="com.example.intent.category.CUSTOM_2" />
<data android:scheme="http"
android:host="com.example.com"
android:port="4000"
android:path="folder"
android:mimeType="text/plain" />
<data android:scheme="content"
android:host="com.example.project"
android.port="1080"
android:path="images"
android:mimeType="image/*" />
</intent-filter>
</activity>

可以看出一个 Activity 可以包含多个 IntentFilter,即形成一个 IntentFilter 列表,只要匹配任何一个 IntentFilter 就可以启动该 Activity。而 IntentFilter 中可以包含 actioncategorydata 三元素,只有这三个都匹配成功,整个 IntentFilter 才能匹配成功。

action 匹配规则

Intent 中的 action 必须和 IntentFilter 中包含的 action 之一匹配就可以匹配成功。如果,把 Intent 中指定的 action 看做元素 a,把 IntentFilter 中指定的 action 所构成的集合看成集合 A,则 当 a ∈ A 时, action 匹配成功。

category 匹配规则

  1. 如果 Intent 指定 category,就必须被包含在 IntentFilter 中所指定的 category。如果,Intent 中指定的 category 元素构成集合 A, IntentFilter 中指定的 category 元素构成集合 S,则当 A ⊆ S 时, category 匹配成功;
  2. 如果 Intent 不指定 category, 则默认的 categoryandroid.intent.category.DEFAULT, 也会匹配成功。

data 匹配规则

data 部分由 mimeTypeURI 组成,mimeType 表示媒体类型,比如图片、音频、视频、文本等格式,URI 的结构为:

1
<scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>]

比如:

1
2
http://com.example.com:8080/search/info
content://com.example.project:200/folder/subfolder/etc
  • schemeURI 的模式,比如 http、file、content,也可以是自定义字符串,如果没有指定 scheme 的值,则整个 URI 无效;
  • hostURI 的主机名,如果没有指定,则整个 URI 无效;
  • port:端口号;
  • pathpathPrefixpathPattern 分别代表 完整路径匹配、前缀路径匹配、正则路径匹配。它们必须需要以 / 开头。

data 的匹配规则跟 action 类似,也就是如果,把 Intent 中指定的 data 看做元素 d,把 IntentFilter 中指定的 data 元素所构成的集合看成集合 D,则 当 d ∈ D 时, data 匹配成功。

如果 data 节点中只指定了 mimeType,那么系统默认的 URIscheme 为 content 和 file。也就是说,在这种情况下,比如 intent 指定的 schemehttp 则会出现 Activity 找不到的异常。