Android 开发艺术探索学习笔记(一)
结合 官方文档 阅读《Android 开发艺术探索》时所做的学习笔记。本篇记录第一章。
Activity 生命周期
- 新 Activity 启动之前,原栈顶 Activity 的 onPause 会先被调用,所以不要在 onPause 中做耗时操作。
- onRestoreInstanceState 方法会在 onStart 之后被调用,推荐在这里而不是 onCreate 方法中恢复数据。大部分 View 都会帮我们做一些基本的状态恢复,但是必须为 View 提供 ID,可以查看对应的 View 中的 onRestoreInstanceState 方法了解具体恢复了哪些内容。
- onSaveInstanceState 方法会在 onStop 之后被调用(targetSdkVersion >= 28),可以在这里保存一些轻量的数据。它可以作为生命周期方法的补充,但是有些时候并不会被调用,比如从 A 中启动 B 的时候,如果 A 未被回收那么系统就不会调用 onSaveInstanceState。
Activity 的启动模式
四种启动模式
- standard:每次都会在当前任务栈中启动一个新的 Activity 实例。使用场景:默认配置,商品详情页。
- singleTop:栈顶复用,再次启动时会调用 onNewIntent() 而不是 onCreate()。使用场景:登录页、通知推送页。
- singleTask:栈内复用,一般与 taskAffinity 结合使用,与 singleTop 类似,如果在请求的目标栈中已有实例存在,则会调用 onNewIntent()。另外,singleTask 启动模式还有 CLEAR_TOP 的效果,比如 A、B 两个 activity 位于同一个任务栈,如果 A 是 singleTask,而且被另一个任务栈的 activity 再次启动,则 B 会出栈。使用场景:App 主页面、WebView 页面、只能出现单一页面的场景。
- singleInstance:加强的 singleTask,只能单独位于一个任务栈中。使用场景:系统 Launcher、锁屏键、来电显示等系统应用。
关于 taskAffinity
- 指定 Activity 在哪个栈中运行,默认使用 applicationId。
- 具有相同 taskAffinity 的 activity 会被放入同一个栈中。
- 一般需要配合 singleTask 启动模式来使用。
关于 allowTaskReparenting
- 当启动该 activity 的时候,如果该 activity 存在相同的 taskAffinity 的栈,则会被移动到那个栈里。比如一个浏览器应用 App A 中的 activity A 指定了 allowTaskReparenting,同时与主 activity 位于同一个任务栈中。那么当你在别的应用 App B 中,点击了一个链接打开了 A,然后再返回桌面打开浏览器应用,会直接打开 activity A 而不是主 activity,而当我们返回 App B 的时候,会发现这个 activity A 已经不在了(被移动到了 App A 的任务栈中)。
- 我们一般结合 singleTask 并指定单独的栈来使用 allowTaskReparenting。
关于 Intent Flags
- FLAG_ACTIVITY_NEW_TASK 对应启动模式中的 singleTask。
- FLAG_ACTIVITY_SINGLE_TOP 对应启动模式中的 singleTop。
- FLAG_ACTIVITY_CLEAR_TOP 具有清除栈中它上面所有 activity 的效果,一般结合 singleTask 使用,可以使得对应任务栈中的 activity 重新响应 intent 以及新传入的数据。
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS,不显示在最近活动中
singleTask 和 singleInstance 的区别
singleTask 启动模式下,当栈和 activity 都不存在的时候,系统会创建一个新的 activity 放入新的栈内,如果栈存在则直接将 activity 入栈,而且除了相同 taskAffinity 的 activity 之外,还允许其他 activity (i.e. standard 和 singleTop 的 activity) 入栈。
singleInstance 启动模式下的 activity 不允许其他 activity 入栈,也就是说栈内永远只有这一个 activity。
IntentFilter 的匹配规则
关于 IntentFilter 的组成
- 一组 IntentFilter 可以由多个
action
category
data
组成,必须同时匹配action
category
data
才能成功启动对应的组件。 - 如果该组件只能处理特定某几种的
action
category
data
组合,那么你就必须创建多个对应的 IntentFilter 而不是把他们全都放一起。
关于 通过匹配测试
- action:指定接收动作类型,用
android:name
指定,可以有 0 个或者多个。一个 Intent 想要匹配成功,则必须至少匹配上一个action
,intent-filter 中可以指定比 Intent 中更多的action
。另外,- 如果 intent-filter 没有指定 action,则所有 Intent 都无法匹配成功;
- 如果 Intent 没有指定 action,那么这个 Intent 会通过所有指定了 action 的 intent-filter。
- cateogry:指定接收分类。如果 Intent 中指定了多个
category
,那么必须全都匹配上才能匹配成功。其他与action
相同。- 使用隐式 Intent 的时候,系统默认添加上一个 CATEGORY_DEFAULT,所以如果你想要匹配成功就必须添加 android.intent.category.DEFAULT
- data:指定 URI 和 MIME type
- URI 包括以下属性:
scheme
,host
,port
,path
,host 有时也称为 authority。- 完整格式:
<scheme>://<host>:<port>/<path>
- 例子:
content://com.example.project:200/folder/subfolder/etc
- 每个属性都是可选的,但是有先后依赖关系,比如如果没有指定
scheme
,那么即使指定了host
或者port
或者path
也是无效的。 - URI 支持部分匹配。
- 完整格式:
- 完整 MIME type 列表可参考 IANA MIME Media Types,不同机型支持的 MIME type 有所不同。
- URI 和 MIME type 匹配有如下规则:
- 完全匹配,比如如果 Intent 只指定了 MIME type,那么所有只指定了 MIME type 都会通过匹配。
- 如果 intent-filter 指定了 MIME type,那么默认支持
content:
orfile:
URI,即如果 Intent 指定了该 MIME type 同时又指定了 scheme 为content
或者file
则可以匹配成功。
- 如果要为 Intent 设置完整的 data 和 type,必须调用 setDataAndType,因为 setData 和 setType 会相互消除。
- URI 包括以下属性:
关于 Intent 匹配
- 我们可以使用
PackageManager
的resove...
以及query...
方法,来确定是否存在可以调用的组件,前者会返回匹配到的最佳组件,后者返回所有匹配到的组件。 - 记得使用 MATCH_DEFAULT_ONLY flag 来过滤掉没有声明 android.intent.category.DEFAULT 的组件。
系列文章
- Android 开发艺术探索学习笔记(一) - 第 1 章:生命周期和启动模式
- Android 开发艺术探索学习笔记(二) - 第 2 章:IPC 机制
- Android 开发艺术探索学习笔记(三) - 第 3~5 章:View 事件机制等
- Android 开发艺术探索学习笔记(四) - 第 6, 7, 12 章:Drawable,动画,Bitmap
- Android 开发艺术探索学习笔记(五) - 第 8, 10, 11 章:Window,线程和线程池,消息机制
- Android 开发艺术探索学习笔记(六) - 第 13~15 章:综合技术,JNI 和 NDK,性能优化