本文主要记录一些与 Activity
相关的,并不是很容易弄清楚的点。
Activity 生命周期
先上 Google 官方的图:
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不作特殊处理,当系统配置发生改变(比如屏幕方向发生改变),Activity就会被销毁并重建,onSaveInstanceState()
和onRestoreInstanceState()
这两个方法就会在这一过程中被调用。内存资源不足导致 Activity 被回收的异常:
Activity会有优先级分级情况,优先级依次降低:- 前台 Acitivty,即正在交互的 Activity
- 可见但非前台 Activity
- 不可见后台 Activity
内存资源不足导致 Activity 被回收,在后续也会通过
onSaveInstanceState()
和onRestoreInstanceState()
存储和恢复数据。
没有四大组件在运行的进程很容易被系统杀死,所以,一些后台工作不适合脱离四大组件独自后台运行,最佳实践是依赖 Service 以保证进程有较高的优先级。
Activity
在异常情况下进行重建时,系统会默认对 View
相关的状态进行存储和恢复,具体会恢复特定 View
的什么状态数据需要去查阅源码。也就是说,非 View
相关的数据,需要自行存储和恢复。
针对上面的第一种情况,Activity
的 configChanges
可以指定一些资源配置属性,当这些资源配置发生改变之后,Activity
不会重建,不会调用 onSaveInstanceState()
和 onRestoreInstanceState()
,取而代之的是回调 onConfigurationChanged()
方法。最常用的 configChanges
选项有:
locale
:设备的本地位置发生改变,一般指切换系统语言;orientation
:屏幕方向发生改变,比如旋转手机屏幕;keyboardHidden
:键盘的可访问性发生改变,比如用户调出键盘。
Activity 启动模式
Activity
的 launchMode
由四种:
standard
:默认启动方式,每次启动这样的Activity
都会生成创建新实例。谁启动以standard
为launchMode
的Activity
, 该Activity
就会运行在它的那个任务栈中;singleTop
:栈顶复用模式,要启动的Activity
已经有实例在栈顶,则不会生成新实例,不会调用onCreate()
和onStart()
而会调用该Activity
的onNewIntent()
方法;其他情况,参考standard
模式;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"/>singleInstance
:单实例模式。可以看做是特殊的singleTask
模式,除了具有singleTask
特性外,此种模式的Activity
只能单独在一个任务栈。
IntentFilter 匹配规则
IntentFilter
用在 Activity
的隐式调用上,先看 IntentFilter
示例:
1 | <activity android:name="com.example.ExampleActivity"> |
可以看出一个 Activity
可以包含多个 IntentFilter
,即形成一个 IntentFilter
列表,只要匹配任何一个 IntentFilter
就可以启动该 Activity
。而 IntentFilter
中可以包含 action
、 category
、 data
三元素,只有这三个都匹配成功,整个 IntentFilter
才能匹配成功。
action 匹配规则
Intent
中的 action
必须和 IntentFilter
中包含的 action
之一匹配就可以匹配成功。如果,把 Intent
中指定的 action
看做元素 a,把 IntentFilter
中指定的 action
所构成的集合看成集合 A,则 当 a ∈ A 时, action
匹配成功。
category 匹配规则
- 如果
Intent
指定category
,就必须被包含在IntentFilter
中所指定的category
。如果,Intent
中指定的category
元素构成集合 A,IntentFilter
中指定的category
元素构成集合 S,则当 A ⊆ S 时,category
匹配成功; - 如果
Intent
不指定category
, 则默认的category
为android.intent.category.DEFAULT
, 也会匹配成功。
data 匹配规则
data
部分由 mimeType
和 URI
组成,mimeType
表示媒体类型,比如图片、音频、视频、文本等格式,URI
的结构为:
1 | <scheme>://<host>:<port>[<path>|<pathPrefix>|<pathPattern>] |
比如:
1 | http://com.example.com:8080/search/info |
scheme
:URI
的模式,比如 http、file、content,也可以是自定义字符串,如果没有指定scheme
的值,则整个URI
无效;host
:URI
的主机名,如果没有指定,则整个URI
无效;port
:端口号;path
、pathPrefix
、pathPattern
分别代表 完整路径匹配、前缀路径匹配、正则路径匹配。它们必须需要以/
开头。
data
的匹配规则跟 action
类似,也就是如果,把 Intent
中指定的 data
看做元素 d,把 IntentFilter
中指定的 data
元素所构成的集合看成集合 D,则 当 d ∈ D 时, data
匹配成功。
如果 data
节点中只指定了 mimeType
,那么系统默认的 URI
的 scheme
为 content 和 file。也就是说,在这种情况下,比如 intent
指定的 scheme
为 http
则会出现 Activity
找不到的异常。