学习内容:
- Activity 的生命周期和启动模式以及 IntentFilter 的匹配规则分析
- 异常情况下的生命周期
- Activity 的启动模式以及 Flags
- 隐式启动下的 Intent 匹配
Activity 的生命周期全面分析
我的另一篇文章:详解 Android&Fragment 的生命周期
此处只是记录一下缺失的知识点,加以扩充。
建议本文与上文配合阅读。
前置
生命周期分为两类:
- 典型情况下的生命周期
- 另一部分是异常情况下的生命周期。
典型情况:指有用户参与的情况下,Activity 所经过的生命周期的改变
异常情况:指 Activity 被系统回收或者由于当前设备的 configuration 发生改变从而导致 Activity 被销毁重建
典型情况下的生命周期
书中的两个问题:
问题一:onStart 和 onResume、onPause 和 onStop 有什么实质性的不同?
答:意义不同:onStart 和 onStop 是从Activity 是否可见这个角度来回调;onResume 和 onPause 是从 Activity 是否处在前台这个角度来回调问题二:当前 Activity 为 A,此时打开新的 Activity B,那么 B 的 onResume 和 A 的 onPause 哪个先执行?
答:旧的Activity(A)执行完 onPause 之后,新的 Activity(B)的 onResume 才能执行。
这个问题在我原来写的那篇文章中也提到过,只不过我得出这个结论是写了个 Demo,测试得来的。而主席这里,从源码分析得到结论,之后再通过实例测试验证结论。可以说,我只是知道了这个结论就是这样,但是却不知道为什么,原理是什么。
异常情况下的生命周期
书中的两种情况:
- 情况1:资源相关的系统配置发生改变导致 Activity 被杀死并重新创建。
- 系统配置发生改变后,Activity 会被销毁,onPause、onStop、onDestroy 正常调用。
- Activity 是异常情况下终止,因而会调用 onSaveInstanceState 方法保存状态。
- 异常情况下终止,需要重建时,系统会默认为我们保存当前 Activity 的视图结构。
- 保存和恢复 View 层级结构的工作流程(以保存为例,恢复过程类似):委托思想
- 首先 Activity 意外终止时,调用 onSavedInstanceState 保存数据
- 然后 Activity 委托 Window 保存数据。
- 接着 WIndow 再委托它上面的顶级容器保存数据(顶级容易是一个 ViewGroup 一般是 DecorView)
- 最后顶级容器一一通知它的子元素来保存数据。
- 情况2:资源内存不足导致低优先级的 Activity 被杀死
- 优先级排序
- 前台 > 可见但非前台 -> 后台
- 后台任务保活:将后台任务放入 Service 从而保证进程有一定的优先级,这样不会轻易被杀死。
- 优先级排序
如何在系统配置发生改变后,避免 Activity 重建?
答:通过为 Activity 指定 configChanges 属性,以此在特定情况下不让系统重建 Activity,此时系统不会保存恢复数据,而是调用了 onConfigurationChanged 方法,在此方法中我们可以做一些自己的特殊处理。
常用的属性为 locale、orientation、keyboardHidden 这三个:locale 指设备本地设置发生改变,一般是系统语言;orientation 指屏幕方向发生改变;keyboardHidden 指键盘的可访问性发生了改变,比如用于调出了键盘。
Activity 的启动模式
我的另一篇文章:深入理解 Android 的启动模式
此处只是记录一下缺失的知识点,加以扩充。
建议本文与上文配合阅读。
Activity 的 LaunchMode
四种模式:
- standard:标准模式
- singleTop:栈顶复用模式
- singleTask:栈内复用模式
- singleInstance:单实例模式
singleTask 下的特殊情况:
假设目前有 2 个任务栈,前台任务栈为 AB,后台任务栈为 CD,且 CD 的启动模式都是 singleTask:
- 如果此时启动 D:整个后台任务栈都被切换到前台,即前台此时为 ABCD。
- 如果此时启动 C:那么 D 会因为 singleTask 模式默认具备 clearTop 的效果而出栈,此时前台任务栈为 ABC。
TaskAffinity 基础
- TaskAffinity 标识 Activit有所需要的任务栈的名字。
- 该属性主要与 singleTask 启动模式或者 allowTaskReparenting 属性配对使用。
TaskAffinity + singleTask
待启动的 Activity 会运行在名字和 TaskAffinity 相同的任务栈中
TaskAffinity + allowTaskReparenting
当一个应用 A 启动了应用 B 的某个 Activity 后,如果这个 Activity 的 allowTaskReparenting 属性为 true 的话,那么当应用 B 被启动后,此 Activity 会直接从应用 A 的任务栈转移到应用 B 的任务栈中。
Activity 的 Flags
见我的另一篇文章:深入理解 Android 的启动模式
IntentFilter 的匹配规则
启动 Activity 分为两种:显式调用和隐式调用。
- 显式调用需要明确指定被启动对象的组件信息,包括包名和类名
- 隐式调用不需要明确指定组件信息。
- 如果二者共存,以显式调用为主。
隐式调用的匹配规则
隐式调用需要 Intent 能够匹配目标组件的 IntentFilter 中所设置的过滤信息,如果不匹配将无法启动目标 Activity。
IntentFilter 中的过滤信息有 action、category、data。
- 当三者都匹配时,IntentFilter 才匹配成功,才能启动目标 Activity。
- 一个 Activity 可以有多个 intent-filter,一个 Intent 只要匹配任何一组 intent-filter 即可成功启动对应的 Activity
1.action 的匹配规则
action 的匹配规则是 Intent 中的 action 必须能够和过滤规则中的 action 匹配,这里匹配指 action 的字符串值完全一致。
一个过滤规则中可能有多个 action,只要 Intent 中的 action 能够和过滤规则中的任何一个 action 相同都可匹配成功。反之,任何一个都不相同,则匹配失败。
需要注意:action 区分大小写。
2.category 的匹配规则
Intent 可以没有 category,但是如果一旦有 category,那么不管有几个,每个都要能和过滤规则中的任何一个 category 相同。
startActivity 或者 startActivityForResult 方法启动 Activity 时,默认会为 Intent 加上 android.intent.category.DEFAULT 这个 category。
3.data 的匹配规则
和 action 的匹配规则类似,Intent 必须含有 data 数据,并且 data 数据能够完全匹配过滤规则中的某一个 data,这里的完全匹配是指过滤规则中出现的 data 部分也出现在了 Intent 中的 data。
先介绍下data 的结构:
data 由两部分组成:mimeType 和 URI。mime Type 指媒体类型,比如 image/jpeg、audio/mpeg4-generic 和 video/* 等,可以表示图片、文本、视频等不同的媒体格式;而 URI 包含的数据很多,URI 的结构如下:
://:/[||]
//实例
content://com.example.project:200/folder/subfolder/etc
http://www.baidu.com:80/search/info
- Scheme:URI 的模式,如 http、file、content等,如果未指定,则 该URI无效。
- Host:URI 的主机名,比如 www.baidu.com,如果未指定,则 该URI无效
- Port:URI 的端口号,比如 80。
- Path、pathPattern 和 pathPrefix:三者表述路径信息,其中 path 表示完整路径信息;pathPattern 也表示完整的路径信息,但是可以包含通配符 "*";pathPrefix 表示路径的前缀信息。
下面说一下 data 的指定:
- 指定完整的 data,必须调用 setDataAndType 方法
- setData 和 setType 互相冲突,会彼此消除对方的值。
避免隐式调用出错
当隐式启动一个 Activity 的时候,如果未加判断,找不到匹配的 Activity,那么会抛出 android.content.ActivityNotFoundException 异常,解决方案:
- PackageManager 的 resolveActivity 方法或者 Intent 的 resolveActivity 方法:返回最佳匹配的 Activity 信息,如果找不到匹配的 Activity 就会返回 null
- PackageManager 的 queryIntentActivities 方法:返回 所有成功匹配的 Activity 信息。
补充说明:
- 不含 DEFAULT 这个 category 的 Activity 无法接收隐式 Intent
- 对于 Service,尽量使用显式调用方式来启动服务
- 通过以下设置表明入口 Activity,并且出现在系统的应用列表: