《Android 开发艺术探索》笔记 - Activity - 01
Activity 生命周期
正常情况下 Activity 的生命周期
onCreate: 表示 Activity 正在被创建, 主要在这个方法中做一些初始化.
onRestart: 表示 Activity 正在重新启动, 一般情况下, 当 Activity 从不可见变为可见时会被调用.
onStart: 表示 Activity 正在启动, 但还无法与用户交互, 此时 Activity 还在后台.
onResume: 表示 Activity 已经可见了, 和 onStart 的区别是现在 Activity 处于前台.
onPause: 当其他 Activity 进入前台时会被调用, 这个方法里不能执行耗时的操作, 否则会影响新 Activity 的启动, 因为新 Activity 的 onResume 会在这个 Activity 的 onPause 执行完成后才会被调用.
onStop: 表示 Activity 即将停止, 可以做一些回收工作, 但也不能太耗时.
onDestroy: 表示 Activity 即将被销毁, 这是最后一个回调.
上图来源于: https://developer.android.google.cn/reference/android/app/Activity.html
Activity 的生命周期回调方法分为一下几种情况:
首次启动,
onCreate -> onStart -> onResume
.当用户切换回桌面或其他 Activity 时,
onPause -> onStop
. 特殊情况: 如果新的 Activity 使用了透明主题, 那么当前 Activity 的onStop
不会被调用.用户再次回到这个 Activity 时,
onRestart -> onStart -> onResume
.当用户按下
返回键
时, 默认情况下 Activity 会被销毁,onPause -> onStop -> onDestroy
.onCreate
和onStop
对应着 Activity 的创建
与销毁
;onStart
和onStop
对应着可见
与不可见
;onResume
和onPause
对应着前台
和后台
.
异常情况下 Activity 的生命周期
- 系统资源发生改变
比如屏幕方向发生变化时. 在默认情况下, Activity 会被销毁, 正常的生命周期方法会被调用, 并且 onSaveInstanceState
和 onRestoreInstanceState
会被额外调用, 用于保存和恢复当前状态.
onSaveInstanceState
会在onStop
之前被调用, onRestoreInstanceState
会在 onStart
之后被调用.
并且, Activity 会自动帮我们保存一些数据, 比如用户在文本框中输入的文本等等.
View 也有 onSaveInstanceState
和 onRestoreInstanceState
两个方法, 当 Activity 意外停止时, 他们会被调用.
- 系统资源不足导致 Activity 被销毁
Activity 的优先级如下:
- 前台 Activity, 即正在和用户交互的 Activity.
- 可见, 但是在后台的 Activity.
- 在后台的 Activity, 并且不可见.
前台 Activity 最不容易被杀死.
在这种情况下, onSaveInstanceState
和 onRestoreInstanceState
也会被调用.
Activity 的启动模式 (Launch Mode)
standard: 标准模式, 也是默认的模式. 可以有多个实例, 在哪个 Activity 栈中启动, 就会被至于那个栈中.
singleTop: 这种模式下, 如果这个 Activtiy 处于栈顶的话, 再次启动这个 Activity 并不会启动一个新的实例, 而这个 Activity 的
onNewIntent
会被调用, 可以在这个方法里处理新的请求.“standard”和“singleTop”Activity 为一类, 使用“standard”或“singleTop”启动模式的 Activity 可多次实例化. 实例可归属任何任务, 并且可以位于 Activity 堆栈中的任何位置.
singleInstance: 该启动模式的 Activity 启动后会创建一个新的任务栈, 并且这个任务栈中始终只会有这一个实例, 如果再次启动, 不会创建新实例, 而
onNewIntent
会被调用.singleTask: 该模式也是一种单实例模式, 如果当前任务栈中不存在这个 Activity , 则会创建这个 Activity, 否则会将已存在的实例调到栈顶, 并且会清除掉原来所有的在该 Activity 之上的 Activity, 如下图:
“singleTask”和“singleInstance”模式同样只在一个方面有差异: “singleTask”Activity 允许其他 Activity 成为其任务的组成部分. 它始终位于其任务的根位置, 但其他 Activity(必然是“standard”和“singleTop”Activity)可以启动到该任务中. 相反, “singleInstance”Activity 则不允许其他 Activity 成为其任务的组成部分.
另外, 在 singleTop
, singleTask
, singleInstance
在栈顶时, 再次启动该 Activity 时, 生命周期调用顺序为: onPause -> onNewIntent -> onResume
.
Activity Flags
- FLAG_ACTIVITY_NEW_TASK
这个 flag 效果与在 xml 中指定 singleTask
效果相同.
- FLAG_ACTIVITY_SINGLE_TOP
这个 flag 效果与在 xml 中指定 singleTop
效果相同.
- FLAG_ACTIVITY_CLEAR_TOP
具有此标记为的 Activity 在启动时, 在同一个任务栈中的所有位于它上面的 Activity 都要出栈.
一般这个 flag 与 FLAG_ACTIVITY_NEW_TASK 配合使用.
如果被启动的 Activity 是 standard
启动模式, 那么连同它自己以及它之上的 Activity 都要出栈.
系统会重新创建该 Activity 的实例, 并放入栈顶.
- FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
这个 flag 可以使启动的 Activity 不在最近任务列表中显示.
IntentFilter 匹配规则
Activity 的启动分为 显式启动 和 隐式启动.
显式启动通过直接指定包名组件名直接启动对应的组件.
隐式启动不需要指定特定的包名或组件名, 可以通过某些信息匹配某些 Activity.
intent-filter 中有三种过滤信息, action, category, data, 而在 startActivity 时, 必须匹配这三个信息, 否则会匹配失败. 一个组件中可以有多个 intent-filter, 只要匹配到其中任意一个即可.
action
action 的匹配要求 Intent 中的 action 字符串与过滤器中声明的相同, 并且区分大小写. 在单个 intent-filter 中可以定义多个 action, 而 Intent 中仅需要匹配到任意一个即可.
category
Intent 可以添加多个 category, Intent 中如果含有 category, 那么 Intent 中添加的 category 必须都在 intent-filter 中声明, 否则会匹配失败.
另外, 在 intent-filter 中必须添加一个 android.intent.category.DEFAULT, 否则它不会被任何 Intent 匹配到, 因为
Android 会自动将 CATEGORY_DEFAULT 类别应用于传递给 startActivity() 和 startActivityForResult() 的所有隐式 Intent. 因此, 如需 Activity 接收隐式 Intent, 则必须将 “android.intent.category.DEFAULT” 的类别包括在其 Intent 过滤器中(如上文的 示例所示).
data
使用一个或多个指定数据 URI 各个方面(scheme、host、port、path 等)和 MIME 类型的属性, 声明接受的数据类型.
语法如下:
<data android:scheme="string" android:host="string" android:port="string" android:path="string" android:pathPattern="string" android:pathPrefix="string" android:mimeType="string" />
scheme
URI 的模式, 比如 http, ftp, file, content 等, 这个是必选项, 如果不指定这个参数, 那么这个 URI 就是无效的.
host
URI 的主机名, 比如 www.google.com, host 也是必须的.
port
主机的端口号, 比如 80.
path | pathPattern | pathPrefix
表示 URI 的路径, path 表示完整的路径, pathPattern 中可以包含通配符, pathPrefix 表示路径的前缀
mimeType
表示文件的类型, 比如
image/png
,text/html
等等.
在被启动的 Activity 中可以通过
Intent#getData
获取到匹配到的 URI.在 Intent 中可以通过
Intent#setData
,Intent#setType
设置 data 或 type. 如果需要同时设置 data 和 type 则需要调用Intent#setDataAndType
, 因为上面两个方法都会清空另一个的值.如果
intents-filters
仅设置了mimeType
, 而没有设置scheme
,scheme
默认会是file
或content
.
在隐式启动过程中, 如果没有匹配到任何组件, 那么程序会报错停止运行. 通常需要在隐式启动时, 判断是否有 Activity 可以匹配到.
有两种方法:
- 通过
PackageManager
的一系列resolve...()
来查询, 如果可以匹配, 则会返回响应 Intent 的最佳组件, 否则就会返回 null. PackageManager
该提供了一系列的query...()
的方法, 它会返回所有可以匹配的 Activity.
两种方法都不会直接激活组件, 而只是列出可以响应的组件.
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
注意第二个参数, 最好使用 MATCH_DEFAULT_ONLY
/**
* Resolution and querying flag: if set, only filters that support the
* {@link android.content.Intent#CATEGORY_DEFAULT} will be considered for
* matching. This is a synonym for including the CATEGORY_DEFAULT in your
* supplied Intent.
*/
public static final int MATCH_DEFAULT_ONLY = 0x00010000;
这个标记的含义是仅仅匹配那些在 intents-filters 中声明了 android.intent.category.DEFAULT 的 Activity, 如果 Activity 没有声明这个 category, 使用 startActivity 将会报错.
参考资料:
https://developer.android.google.cn/guide/topics/manifest/activity-element.html
https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
http://blog.csdn.net/javazejian/article/details/52072131
https://developer.android.com/guide/components/intents-filters.html