【Android】Activity 的生命周期和启动模式
目录
Activity 生命周期
具体场景
Activity 的首次启动
用户打开新的 Activity 或返回桌面
用户回到 Activity
异常情况
资源相关的系统配置发生改变导致 Activity 被杀死并重新创建
资源内存不足导致低优先级的 Activity 被杀死
Activity LaunchMode
standard 标准模式
singleTop 栈顶复用模式
singleTask 栈内复用模式
singleInstance 单实例模式
allowTaskReparenting 属性
指定启动模式
Flags 标志位
IntentFilter 过滤规则
action 的匹配规则
category 的匹配规则
data 的匹配规则
匹配判断
Activity 生命周期
Activity 生命周期回调方法的执行顺序是:
onCreate -> onRestart -> onStart -> onResume -> onPause -> onStop -> onDestory
onCreate & onDestory // Activity 初始化和回收
onStart & onStop // Activity 是否可见
onResume & onPause // Activity 是否可交互
具体场景
Activity 的首次启动
onCreate -> onStart -> onResume
用户打开新的 Activity 或返回桌面
onPause -> onStop
用户回到 Activity
onRestart -> onStart -> onResume
当前 Activity 为 A,此时用户打开新的 Activity B,A 的 onPause 先于 B 的 onStart 执行,因为 Android 的设计原则:任何时刻都只有 1 个 Activity 处于 Resumed 阶段,如果 A 的 onPause 晚于 B 的 onResume 执行,那么会出现 A 和 B 同时处于 Resumed 阶段的瞬间,用户输入事件在此时不知道交付给哪个 Activity 来响应。
异常情况
资源相关的系统配置发生改变导致 Activity 被杀死并重新创建
最典型的情况就是用户旋转屏幕,回调顺序为
onPause -> onStop -> onDestory -> onCreate -> onStart -> onResume
onSaveInstanceState 👆 👇 onRestoreInstanceState
其中 onSaveInstanceState 的调用时机在 onStop 前,onRestoreInstanceState 的调用时机在 onStart 后,我们在 Android Studio 空白 Activity 的模板代码中能看到
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);EdgeToEdge.enable(this);setContentView(R.layout.activity_main);ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);return insets;});}
}
onCreate 回调方法的参数 Bundle 是用于存储 View 状态信息的轻量化键值对结构,在资源配置改变的异常行为发生时,系统会调用 onSaveInstanceState 回调方法来将此时 View 的内容保存成 Bundle 的形式,如果是新创建的 Activity,则 onCreate 的 Bundle 参数为 null,如果是异常情况重新创建,则 onCreate 的 Bundle 参数为 onSaveInstanceState 保存的 Bundle 实例。
系统框架会在 onRestoreInstanceState 阶段自动恢复控件信息,开发者可以重写 onRestoreInstanceState 方法来拿取自己的业务逻辑数据,相应在 onSaveInstanceState 方法存储,onCreate 和 onRestoreInstanceState 传入的 Bundler 实例是同一个实例,它们都来自系统在异常销毁前 onSaveInstanceState 保存的状态。开发者应该在 onCreate 恢复不依赖于视图树的业务数据,同时在 onRestoreInstanceState 恢复必须依赖完整 View 初始化的状态信息。
资源内存不足导致低优先级的 Activity 被杀死
因为同样属于 Activity 被系统回收的异常销毁行为,所以数据恢复过程和资源配置改变的情况完全一致。Activity 的优先级从高到低划分即是生命周期回调的 3 个阶段:Resumed、Start 和 Stop,也就是焦点活动 > 可见活动 > 后台活动。如果一个进程中没有组件正在运行,那么这个进程会很快被系统杀死,比较好的方法是让该进程使用 Service 执行后台工作,这样可以避免被系统轻易杀死。
如果开发者不希望系统配置改变时 Activity 重新创建,则可以使用 android:configChanges 属性来指定不希望重新创建的场景,如 andorid:configChanges="orientation"
可以避免在屏幕方向发生改变时重启 Activity,系统不再帮助自动恢复视图,开发者要重写 onConfigurationChanged 回调方法来手动更新布局资源,如果没有重写,则界面不会刷新,如从竖屏变横屏,布局依然会显示竖屏布局。
Activity LaunchMode
standard 标准模式
系统默认模式,每次启动 Activity 都会重新创建新的 Activity 实例,如果 A 启动 B,则 B 会运行在 A 所在的栈中,如果使用 ApplicationContext 启动标准模式的 Activity 时会报错,因为非 Activity 的 Context 没有任务栈。
singleTop 栈顶复用模式
如果启动的 Activity 已经位于任务栈的栈顶,那么该 Activity 不会被重新创建。
singleTask 栈内复用模式
只要启动的 Activity 在一个栈中存在,那么多次启动该 Activity 都不会重新创建实例,有多种情况
-
任务栈 S1 的情况是 ABC,D 以栈内复用模式请求启动进 S2,则系统会先创建 S2,然后再创建 D 的实例并将其放入 S2。
-
任务栈 S1 的情况是 ABC,D 以栈内复用模式请求启动进 S1,则系统会直接创建 D 的实例并将其放入 S1,完成操作后 S1 的情况是 ABCD。
-
任务栈 S1 的情况是 ADBC,D 以栈内复用模式请求启动进 S1,则系统会将 D 顶的所有 Activity 实例全部出栈,最终 S1 的情况是 AD。
singleInstance 单实例模式
要引入前台和后台的概念,前台任务栈是指栈顶 Activity 处于 Resumed 阶段的任务栈,前面我们提到,任意时刻处于 Resumed 阶段的 Activity 是唯一的,所以前台任务栈也是唯一的,后台任务栈是指栈顶 Activity 处于 Stopped 阶段的任务栈,后台任务栈可以有多个。每次启动 Activity,启动的 Activity 绝大多数情况都处于前台任务栈中。
所以启动单实例模式的 Activity,系统会创建新的前台任务栈和 Activity 实例,并将该 Activity 放入前台任务栈,原本位于前台的任务栈进入后台。也因为前后台任务栈的概念,singleTask 模式有一种特殊情况
-
前台任务栈情况 AB,后台任务栈情况 CD,C 以 singleTask 模式请求启动,那么 C 所在的后台任务栈首先进行出栈操作直到 C 位于栈顶,再将该任务栈直接切换到前台,此时前台任务栈的情况是 ABC
allowTaskReparenting 属性
Activity 所需要的任务栈即 TaskAffinity 偏好任务栈,默认是应用包名,在启动新的 Activity 时,如果 TaskAffinity 与目标任务栈匹配,则会尝试放入该栈里。allowTaskReparenting 属性仅在 Activity 启动后处于后台时有效,表示系统是否允许 Activity 从当前启动的任务栈迁移到与它的 TaskAffinity 匹配的任务栈。
如果应用 A 启动应用 B 的某个 Activity,该 Activity 的 allowTaskReparenting 属性为 true、TaskAffinity 为默认的 B 包名,则应用 B 启动后该 Activity 会直接从 A 的任务栈转移到 B 的任务栈。
指定启动模式
-
AndroidMenifest.xml
<activityandroid:name="main_activity"android:launchMode="singleTask" />
-
Intent 标志位
Intent intent = new Intent(); intent.setClass(this, SecondActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);
onNewIntent 是 Activity 的回调方法,用来处理已经存在的 Activity 再次被新的 Intent 启动的情况。标志位的优先级大于属性,二者同时存在时,以标志位情况为准。
Flags 标志位
FLAG_ACTIVITY_NEW_TASK
指定 singleTask 启动模式。
FLAG_ACTIVITY_SINGLE_TOP
指定 singleTop 启动模式。
FLAG_ACTIVITY_CLEAR_TOP
具有此标志位的 Activity 启动时,所在任务栈顶所有 Activity 都要出栈。
FLAG_ACTIIVITY_EXCLUDE_FROM_RECENTS
具有此标志位的 Activity 不会出现在最近任务列表中,等效于 android:excludeFromRecents="true"
属性。
IntentFilter 过滤规则
启动 Activity 分为显式调用和隐式调用两种,二者共存的情况下以显式调用为主,隐式调用的 Intent 能够匹配启动组件的 IntentFilter 所设置的过滤信息。过滤信息分别有 action、category 和 data 等,可以有多个,同类别信息共同约束当前类别的匹配规则,只有完全匹配所有匹配规则才能成功启动目标 Activity,Activity 可以有多个 IntentFilter,一个 Intent 只要能匹配任意组 IntentFilter 即可成功启动对应 Activity。
action 的匹配规则
action 是字符串,系统预定义了部分 action,action 区分大小写,匹配要求 Intent 的 action 存在且与过滤规则中的其中一个 action 完全相同。
category 的匹配规则
category 也是字符串,与 action 不同的是,category 是对 action 行为的行为限制或使用场景约束,它要求 Intent 中如果含有 category,那么 Intent 所有的 category 都必须与过滤规则中的任意一个 category 相同,Intent 可以没有 category。所以只要 Intent 的 category 是 IntentFilter 的子集即可成功匹配。
为什么没有 category 也可以匹配?原因是系统在启动 Activity 时会默认为 Intent 加上 android.intent.category.DEFAULT 这个 category,所以为了 Activity 能够接收隐式调用,必须在 intent-filter 指定 DEFAULT 类别。
data 的匹配规则
<data android:scheme="string"android:host="string"android:port="string"android:path="string"android:pathPattern="string"android:pathPrefix="string"android:mimeType="string" />
data 由媒体类型 mimeType 和 URI 组成,URI 的结构是 <scheme>://<host>/[<path>|<pathPrefix>|<pathPattern>]
,data 的匹配规则与 action 类似,要求 Intent 中必须含有 data 数据且能够完全匹配过滤规则中的其中一个的 data。只设置 mimeType 部分时,系统在 Intent 内部会补齐空的 content://
,对应 data 的 scheme 部分。
匹配判断
可以使用 PackageManage 或 Intent 的 resolveActivity 方法来判断是否有匹配的 Activity,如果没有则返回 null,有则返回最佳匹配的 Activity 信息,PackageManage 还有 queryIntentActivities 方法来返回所有匹配成功的 Activity 信息列表,方法签名分别是
public abstract List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
public abstract ResolveInfo resolveActivity(Intent intent, int flags);
参考内容:Android 开发艺术探索 Ch.1