【Android】深入理解Activity的生命周期和IntentFilter
文章目录
- 【Android】深入理解Activity的生命周期和IntentFilter
-
- [1. Activity的生命周期](#1. Activity的生命周期)
-
- [1.1 典型情况下的生命周期分析:](#1.1 典型情况下的生命周期分析:)
- [1.2 异常情况下的生命周期分析](#1.2 异常情况下的生命周期分析)
-
- [1.2.1 资源相关的系统配置发生改变导致Activity被杀死并重新创建](#1.2.1 资源相关的系统配置发生改变导致Activity被杀死并重新创建)
- [1.2.2 资源内存不足导致低优先级的Activity被杀死](#1.2.2 资源内存不足导致低优先级的Activity被杀死)
- [2. 活动的启动模式](#2. 活动的启动模式)
- [3. IntentFilter的匹配规则](#3. IntentFilter的匹配规则)
-
- [3.1 Action的匹配规则](#3.1 Action的匹配规则)
- [3.2 Category的匹配规则](#3.2 Category的匹配规则)
- [3.3 Data的匹配规则](#3.3 Data的匹配规则)
1. Activity的生命周期
Android中的活动是层叠的,每当我们启动一个新活动,就会覆盖之前的活动。当我们按下back键将当前活动销毁后,之前的活动又会再一次展现出来。而Android是通过任务(Task)来管理活动的,一个任务就是存放在栈里边的活动的集合。这个栈也被称作返回栈(Back Stack)。
Activity在进栈与出栈的过程中国,一般意义上有四种状态:
- 当Activity处于栈顶时,此时正好处于屏幕最前方,此时处于运行状态。
- 当Activity失去了焦点但仍然部分可见(如栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕),此时处于暂停状态。
- 当Activity被其他Activity完全遮挡,此时此Activity对用户不可见,此时处于停止状态。
- 当Activity由于人为或系统原因(如低内存等)被销毁,此时处于销毁状态。
1.1 典型情况下的生命周期分析:
在正常情况下,Activity会经历如下生命周期:
- onCreat():当Activity第一次被创建时调用,完成活动的初始化操作。
- onStart():表示Activity正在被启动,即将开始。这时Activity已经可见了,但还没有出现在前台,无法和用户交互。
- onResume():表示Activity已经可见了,并且出现在前台并开始活动。
- onPause():表示Activity正在停止,正常情况下,紧接着onStop就会被调用。
- onStop():表示Activity即将停止,当Activity完全不可见时调用。
- onDestory():Activity被销毁时调用。
- onRestart():表示Activity正在重新启动,在Activity由停止状态变为运行状态时调用。

注:
onResume()和onStart()的区别:onStart()和onResume()都表示Activity已经可见,但是onStart()的时候Activity还在后台,onResume的时候才显示到前台。- 当启动一个新Activity时,一定是老活动的onPause方法先执行,新活动的onResume才会执行。
- 在onPause和onStop方法中进行的资源回收工作不能太耗时。
Activity的3种生存期
- **完整生命周期:活动在 onCreat()方法和onDestory()**方法之间所经历的,就是完整生命周期。一般情况下,活动会在onCreate()方法中完成各种初始化,在onDestory()方法中完成资源释放的操作。
- 前台生命周期:活动在onResume()方法和onPause()方法之间所经历的就是前台生命周期。在前台生命周期内,活动总是处于运行状态,此时的活动可以和用户进行交互。
- 可视生命周期:活动在onStart()方法和onStop()方法之间所经历的,就是可视生命周期。在可视生命周期内,活动对于用户时可见的,但可能与用户无法交互。

Activity的启动流程
主要包含以下几个部分:
- 发起者进程:当前正在前台的应用进程。
- 系统进程:Android系统的核心,运行着关键系统服务,特别是ActivityTaskManagerService(ATMS),负责协调和调度整个流程。
- Zygote进程:所有应用进程的"孵化器",预加载了通用框架和资源,用于快速构建出新应用进程。
- 目标进程 :将要启动的Activity所在的应用进程。它内部运行着:
- ActivityThread:应用的主线程,是每个应用的真正入口。负责接收系统服务(ATMS)的指令,并调用相应的Activity生命周期方法。
- Instrumentation:负责创建Activity实例和调用生命周期方法。每个应用只有一个Instrumentation实例。
- Activity:最终要启动的目标对象。
主要过程:
- 发起请求 :Activity A调用
startActivity(),请求通过Binder IPC发送到系统进程(ATMS)。 - 系统调度:ATMS解析Intent、校验权限。如果目标应用进程不存在,则请求Zygote进程构建一个新进程。
- 进程准备:新进程创建后,初始化主线程(ActivityThread)并通知ATMS,建立通信链路。
- 执行指令 :ATMS通过Binder通知目标进程的
ActivityThread启动目标Activity。 - 创建与回调 :目标进程的主线程通过Handler消息,使用
Instrumentation工具类反射创建Activity实例 ,并依次同步调用其生命周期方法:onCreate()->onStart()->onResume()。 - 界面渲染 :在
onResume()之后,将Activity的根视图(DecorView)添加到WindowManager,最终界面显示出来。
1.2 异常情况下的生命周期分析
1.2.1 资源相关的系统配置发生改变导致Activity被杀死并重新创建
最常见的异常生命周期场景就是配置变更 ,比如屏幕旋转、键盘可用性改变、语言切换等。这会导致Activity被销毁并重建。生命周期如下图:

onSaveInstanceState(Bundle outBundle):在Activity即将被异常销毁前(在onStop之前,可能在onPause前后)调用。需要将希望恢复的数据(如编辑框的临时内容、游戏分数)存入Bundle中。这个方法不会在用户主动销毁(如按返回键)时调用。onRestoreInstanceState(Bundle savedInstanceState):在Activity被异常销毁后重建时,在onStart之后被调用,用于恢复数据。你也可以在onCreate中恢复数据,但onRestoreInstanceState被调用时,视图层级已经创建完成,有时更方便。
1.2.2 资源内存不足导致低优先级的Activity被杀死
Activity按照优先级从低到高,可以分为以下3种:
- 前台Activity:正在和用户交互的Activity,优先级最高。
- 可见但非前台Activity:比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户进行直接交互。
- 后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低。
当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceState和onRestoreInstanceState来存储和恢复数据。
生命周期表现 :如果是后台Activity被杀,则此时没有任何生命周期回调,onDestory不会被调用。如果用户返回该Activity,如果此时进程还存活,系统会直接调用前台Activity的onRestart-> onStart-> onResume。如果进程已被系统杀死:系统会重新创建进程,并尝试重建任务栈顶的Activity(即用户上次看到的那个)。此时的生命周期与屏幕旋转类似:
onCreate(savedInstanceState)- 这里的savedInstanceState是系统在杀死进程前,自动调用所有Activity的onSaveInstanceState保存下来的一个Bundle(称为"墓碑"状态)。- 之后是
onStart,onRestoreInstanceState,onResume。
2. 活动的启动模式
standard(标准模式) :默认模式 。每次启动都创建一个新实例。singleTop(栈顶复用) :如果要启动的Activity正好在栈顶 ,则直接复用,不会新建。否则,创建新实例。singleTask(栈内单例) :在整个任务栈中只保留一个实例 。如果已存在,则清空它上面的所有Activity,让它回到栈顶。singleInstance(全局单例) :最特殊。让Activity独占一个全新的任务栈,并且该栈中只有它一个Activity。
3. IntentFilter的匹配规则
IntentFilter即"意图过滤器",只有IntentFilter中的action,category,data都匹配时,才能成功启动目标Activity。另外,一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity。比如下面的例子:
xml
<activity android:name=".TargetActivity">
<intent-filter>
<action android:name="com.example.action.MY_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
在另一个活动中发起Intent:
java
Intent intent = new Intent();
intent.setAction("com.example.action.MY_ACTION");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setType("text/plain");
startActivity(intent);
可以看到,三个规则都会成功匹配,所以最后可以成功启动TargetActivity。
3.1 Action的匹配规则
Intent 中指定的 Action 字符串,必须出现在 Filter 声明的 Action 列表中。Intent只能通过 intent.setAction(String)设置一个 Action。而Filter可以通过多个 <action>标签声明多个 Action。只要 Intent 的 Action 与 Filter 中声明的任意一个 Action 字符串完全相等,则 Action 匹配成功。
需要注意的是,如果 Intent 没有设置 Action,则自动匹配失败。第二,如果 Filter 没有声明任何 Action,则只能匹配没有设置 Action 的 Intent。
使用示例如下:
xml
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="com.example.action.MY_CUSTOM_ACTION" />
</intent-filter>
java
intent.setAction("android.intent.action.VIEW"); // 匹配成功(与第一个action匹配)
intent.setAction("com.example.action.MY_CUSTOM_ACTION"); // 匹配成功(与第二个action匹配)
intent.setAction("some.other.action"); // 匹配失败
3.2 Category的匹配规则
Intent 中携带的每一个 Category,都必须能在 Filter 声明的 Category 列表中找到。和Action不同的是,Intent可以通过 intent.addCategory(String)添加多个 Category。Filter也可以通过多个 <category>标签声明多个 Category。当Intent 中所有的 Category 在 Filter 中有声明,即 Intent 的 Category 集合是 Filter 的 Category 集合的子集时,匹配成功。Filter 声明的 Category 可以比 Intent 携带的更多。
注: CATEGORY_DEFAULT通过 startActivity()发起的隐式 Intent,系统会自动为其加上 android.intent.category.DEFAULT这个 Category。因此,如果 Activity 希望被隐式 Intent 启动,必须在 Filter 中声明 <category android:name="android.intent.category.DEFAULT" />,否则永远无法匹配。
比如,
xml
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
intent设置时,系统会自动添加 CATEGORY_DEFAULT:
java
intent.addCategory(Intent.CATEGORY_DEFAULT); // 匹配成功
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory(Intent.CATEGORY_BROWSABLE); // 匹配成功
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.addCategory("com.example.category.CUSTOM"); // 匹配失败(CUSTOM不在Filter的声明中)
3.3 Data的匹配规则
Data 由两部分组成:URI 和 MIME 类型。
Intent 的 Data 必须满足 Filter 中声明的某一项 <data>规则。比如:
xml
<data android:scheme="http"
android:host="example.com"
android:mimeType="text/plain"/>
这个标签实际上定义了一组条件:Scheme 必须是 http,Host 必须是 example.com,且 MIME 类型必须是 text/plain。
匹配过程如下:
系统首先会比较 MIME Type(MIME Type 优先)。如果 Filter 声明了 mimeType,则 Intent 的 Type 必须与之匹配(支持通配符)。如果 MIME Type 匹配或者 Filter 未声明 MIME Type,则开始匹配 URI。URI 的匹配是逐级进行的:
- Scheme(协议,如 http、content、file):必须完全匹配。
- Host (主机名,如 www.example.com):必须完全匹配,支持通配符
*。 - Port(端口,如 8080):必须完全匹配。如果未指定,则使用 Scheme 的默认端口。
- Path (路径,如 /images/logo.png):可以完全匹配,也可以使用通配符
*进行前缀匹配。
举例说明一下:
xml
<intent-filter>
<!-- 规则1 -->
<data android:scheme="content" android:mimeType="video/*"/>
<!-- 规则2 -->
<data android:scheme="http" android:host="*.example.com" android:pathPrefix="/movies"/>
</intent-filter>
java
intent.setDataAndType(Uri.parse("content://media/videos/1"), "video/mp4"); // 匹配成功(scheme和mimeType都匹配规则1)
intent.setDataAndType(Uri.parse("http://foo.example.com/movies/avatar.mp4"), null); // 匹配成功(scheme, host, path都匹配规则2)
intent.setDataAndType(Uri.parse("http://google.com"), "text/html"); // 匹配失败(host不匹配规则2,scheme和mimeType不匹配规则1)