【Android】深入理解Activity的生命周期和IntentFilter

【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在进栈与出栈的过程中国,一般意义上有四种状态:

  1. 当Activity处于栈顶时,此时正好处于屏幕最前方,此时处于运行状态
  2. 当Activity失去了焦点但仍然部分可见(如栈顶的Activity是透明的或者栈顶Activity并不是铺满整个手机屏幕),此时处于暂停状态
  3. 当Activity被其他Activity完全遮挡,此时此Activity对用户不可见,此时处于停止状态
  4. 当Activity由于人为或系统原因(如低内存等)被销毁,此时处于销毁状态

1.1 典型情况下的生命周期分析:

在正常情况下,Activity会经历如下生命周期:

  1. onCreat():当Activity第一次被创建时调用,完成活动的初始化操作。
  2. onStart():表示Activity正在被启动,即将开始。这时Activity已经可见了,但还没有出现在前台,无法和用户交互。
  3. onResume():表示Activity已经可见了,并且出现在前台并开始活动。
  4. onPause():表示Activity正在停止,正常情况下,紧接着onStop就会被调用。
  5. onStop():表示Activity即将停止,当Activity完全不可见时调用。
  6. onDestory():Activity被销毁时调用。
  7. onRestart():表示Activity正在重新启动,在Activity由停止状态变为运行状态时调用。

注:

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

主要包含以下几个部分:

  1. 发起者进程:当前正在前台的应用进程。
  2. 系统进程:Android系统的核心,运行着关键系统服务,特别是ActivityTaskManagerService(ATMS),负责协调和调度整个流程。
  3. Zygote进程:所有应用进程的"孵化器",预加载了通用框架和资源,用于快速构建出新应用进程。
  4. 目标进程 :将要启动的Activity所在的应用进程。它内部运行着:
    • ActivityThread:应用的主线程,是每个应用的真正入口。负责接收系统服务(ATMS)的指令,并调用相应的Activity生命周期方法。
    • Instrumentation:负责创建Activity实例和调用生命周期方法。每个应用只有一个Instrumentation实例。
    • Activity:最终要启动的目标对象。

主要过程:

  1. 发起请求 :Activity A调用 startActivity(),请求通过Binder IPC发送到系统进程(ATMS)。
  2. 系统调度:ATMS解析Intent、校验权限。如果目标应用进程不存在,则请求Zygote进程构建一个新进程。
  3. 进程准备:新进程创建后,初始化主线程(ActivityThread)并通知ATMS,建立通信链路。
  4. 执行指令 :ATMS通过Binder通知目标进程的 ActivityThread启动目标Activity。
  5. 创建与回调 :目标进程的主线程通过Handler消息,使用 Instrumentation工具类反射创建Activity实例 ,并依次同步调用其生命周期方法:onCreate()-> onStart()-> onResume()
  6. 界面渲染 :在 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种:

  1. 前台Activity:正在和用户交互的Activity,优先级最高。
  2. 可见但非前台Activity:比如Activity中弹出了一个对话框,导致Activity可见但是位于后台无法和用户进行直接交互。
  3. 后台Activity:已经被暂停的Activity,比如执行了onStop,优先级最低。

当系统内存不足时,系统就会按照上述优先级去杀死目标Activity所在的进程,并在后续通过onSaveInstanceStateonRestoreInstanceState来存储和恢复数据。

生命周期表现 :如果是后台Activity被杀,则此时没有任何生命周期回调,onDestory不会被调用。如果用户返回该Activity,如果此时进程还存活,系统会直接调用前台Activity的onRestart-> onStart-> onResume。如果进程已被系统杀死:系统会重新创建进程,并尝试重建任务栈顶的Activity(即用户上次看到的那个)。此时的生命周期与屏幕旋转类似:

  1. onCreate(savedInstanceState)- 这里的savedInstanceState是系统在杀死进程前,自动调用所有Activity的onSaveInstanceState保存下来的一个Bundle(称为"墓碑"状态)。
  2. 之后是 onStart, onRestoreInstanceState, onResume

2. 活动的启动模式

  1. standard(标准模式)默认模式 。每次启动都创建一个新实例
  2. singleTop(栈顶复用) :如果要启动的Activity正好在栈顶 ,则直接复用,不会新建。否则,创建新实例。
  3. singleTask(栈内单例) :在整个任务栈中只保留一个实例 。如果已存在,则清空它上面的所有Activity,让它回到栈顶。
  4. 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 由两部分组成:URIMIME 类型

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)
相关推荐
啃火龙果的兔子1 小时前
安卓从零开始
android
CryptoRzz2 小时前
印度股票数据 PHP 对接文档 覆盖 BSE(孟买证券交易所)和 NSE(印度国家证券交易所)的实时数据
android·服务器·开发语言·区块链·php
lkbhua莱克瓦242 小时前
集合进阶6——TreeMap底层原理
java·开发语言·笔记·学习方法·hashmap
JEECG低代码平台2 小时前
GitHub 十大 Java 语言 AI 开源项目推荐
java·人工智能·github
小咖张2 小时前
idea 启动失败,不加载自己的配置文件
java·ide·intellij-idea
m***11902 小时前
使用IDEA环境编译Spring源码及spring源码调试环境搭建
java·spring·intellij-idea
代码程序猿RIP2 小时前
【C++开发面经】全过程面试问题详解
java·c++·面试
安卓蓝牙Vincent2 小时前
Android多SDK合并为单个JAR包的完整指南
android
whatever who cares2 小时前
Java/Android中BigDecimal的相关操作
android·java·开发语言