四、意图

Intents 是 Android 中的核心进程间通信(IPC)机制之一,允许应用与其他 Android 组件(包括应用)进行通信(例如,发送数据或启动操作),即使接收者当前没有运行。Android 中有两个主要的意图类别,它们是:

  • Explicit——显式意图是指定应用或应用和组件的意图,应用和组件将执行请求。

  • 隐式 -隐式意图更加模糊,指定了所需的动作类型(例如,打开相机或定位应用)。

除了这两个主要类别的意图(其中消息被直接发送到特定的应用或服务)之外,还可以发送广播意图。这些广播消息可以由 Android 系统或应用发送,同时由设备上先前注册了特定广播动作类型的所有应用接收。在某些情况下,可能需要特殊权限来注册特定的广播(例如,在BOOT_COMPLETE权限的情况下,当 Android 系统在启动后完成加载时,允许接收ACTION_BOOT_COMPLETED意图)。

启动组件

除了这两类意图之外,还有三种发送意图的主要方法:

  • 启动一个活动——一个活动的实例可以通过将一个初始化的 Intent 对象传递给startActivity()startActivityForResult()上下文方法来启动。

  • 启动后台服务——在 Android 5.0 (API level 21)之前,可以通过将初始化的Intent对象传递给startService()上下文方法来启动后台Service。API 级以后,这个可以用来启动JobScheduler组件。

  • 发送广播——虽然系统以固定的频率发送许多广播,如TIME_TICKBOOT_COMPLETE,但普通应用也可以发送自己的广播。一般来说,广播是一种可以被多个应用同时接收的意图。这种广播可以用sendBroadcast()sendOrderedBroadcast()上下文方法发送。

意图属性

意图由几个属性组成。根据发送的意图和接收者,以下一些属性可能是强制的;然而,在某些情况下,这些都不是必需的。所有标准意图属性都作为常量存储在Intent对象中,例如Intent.FLAG_ACTIVITY_NO_HISTORY,其常量值为1073741824

核心属性

  • 动作 -可使用已初始化的Intent对象的setAction方法设置。该操作定义了要执行的高级操作。

  • 数据 -可使用初始化的意图对象的setData方法设置。数据字段包括该意图正在操作的数据(例如,要在图像编辑应用中打开的文件)。

附加属性

  • 类别 -可使用初始化的Intent对象的addCategory方法设置。类别提供了关于意图要执行的动作的附加上下文。只有能够促进所有指定类别的活动才能被选择来接收意图。

  • 类型 -可使用初始化的Intent对象的setType方法设置。通常类型是从数据本身推断出来的;然而,该属性可用于设置特定的 MIME(多用途互联网邮件扩展)类型(例如,audio/mpeg or audio/*),例如,要返回的数据。

  • 组件 -可使用初始化的Intent对象的setComponent方法设置。此属性标识用于意图的组件类的名称。这是一个可选属性,因为它通常是基于意图的内容来标识的。

  • 额外的 -可使用初始化的Intent对象的putExtra方法设置。extra 是接收者可以使用的捆绑包(一组不同类型的键值对)(例如,当与您的社交媒体应用共享笔记时,笔记文本将作为捆绑包中的字段发送)。这个包可以包含专有密钥或在 Android 系统中设置的密钥,如EXTRA_ALARM_COUNT

  • 标志 -可使用初始化的意图对象的setFlag方法设置。标志表示正在启动的组件的行为(例如,不包括任务堆栈中的已启动活动)。

行动

可使用初始化的Intent对象的setAction方法设置。getAction方法也可以用于检索接收到的意图的动作。该操作定义了要执行的高级操作。

这里可以看到这样一个例子:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);

在撰写本文时,Android 文档 1 列出了 135 种以上的标准意图动作类型。表 4-1 列出了最常见意图动作的子集。

表 4-1

意图动作

|

名字

|

API 级别

|

描述

| | --- | --- | --- | | 操作 _ 所有 _ 应用 | 1+ | 列出所有可用应用。 | | 动作 _ 应用 _ 错误 | 14+ | 当用户在崩溃对话警告消息中选择“报告”按钮时发生。然后,该意图将被发送到安装出错应用的安装应用。 | | 动作 _ 摄像机 _ 按钮 | 1+ | 指示设备的“相机按钮”被按下的广播。 | | 动作选择器 | 1+ | 系统提供的默认标准活动选择器的替代选择。反过来,这个动作将显示一个活动选择器。 | | 动作 _ 默认动作 _ 视图 | 1+ | 向用户显示数据的简单操作。 | | 行动 _ 我的 _ 包 _ 暂停 | 28+ | 一种受保护的广播意图,只能由系统发送,并在应用进入暂停状态时发送给应用。 | | ACTION _ MY _ PACKAGE _ UNSUSPENDED | 28+ | 一种受保护的广播意图,只能由系统发送,并在应用离开挂起状态时发送给应用。 | | 行动 _ 包 _ 已添加 | 1+ | 一种受保护的广播意图,只能由系统发送,当设备上安装了新的应用时发送,包括两个附加项:EXTRA_UIDEXTRA_REPLACING。 | | 操作 _ 包 _ 已更改 | 1+ | 受保护的广播意图,只能由系统发送,并且在设备上的现有应用发生变化时发送,包括额外的:EXTRA_UIDEXTRA_CHANGED_COMPONENT_NAME_LISTEXTRA_DONT_KILL_APP。 | | 动作 _ 包 _ 数据 _ 清除 | 3+ | 受保护的广播意图,只能由系统发送,并且应该以ACTION_PACKAGE_RESTARTED开头。当应用的持久数据被擦除时,这个广播被发送,记住这个意图不是发送给应用本身,包括额外的:EXTRA_UIDEXTRA_PACKAGE_NAME。 | | 动作 _ 包 _ 第一个 _ 启动 | 12+ | 一种受保护的广播意图,只能由系统发送,并在首次启动应用时发送给应用的安装程序(例如,谷歌 Play 商店)。不包括额外费用;但是,该数据包括启动的包的名称。 | | 操作 _ 包 _ 重新启动 | 1+ | 一种受保护的广播意图,只能由系统发送,并在用户终止应用及其所有进程时触发。该数据将包含包的名称,还将包含额外的EXTRA_UID。 | | 操作 _ 包 _ 已删除 | 1+ | 一种受保护的广播意图,只能由系统发送,并在应用从设备中移除时触发。由于应用已被删除,它将不会收到意向。除了包含应用名称的数据属性之外,还将包含以下附加内容:EXTRA_UIDEXTRA_DATA_REMOVEDEXTRA_REPLACING。 | | 动作 _ 电源 _ 已连接 | 4+ | 一种受保护的广播意图,只能由系统发送,表示设备已连接到电源。 | | 动作 _ 电源 _ 断开 | 4+ | 一种受保护的广播意图,只能由系统发送,表示设备已断开电源。 | | 操作 _ 重新启动 | 1+ | 一种受保护的广播意图,只能由系统发送,并指示设备重新启动。 | | 动作 _ 运行 | 1+ | 运行已定义数据的高级操作。 | | 动作 _ 屏幕 _ 关闭 | 1+ | 一种受保护的广播意图,只能由系统发送,并在设备屏幕进入睡眠状态或变为非活动状态时发送。 | | 动作 _ 关闭 | 4+ | 受保护的广播意图,只能由系统发送,并在设备关闭过程中触发。 | | 动作 _ 发送 | 1+ | 向收件人发送数据的高级操作。这个动作通常与选择器成对出现。 | | 动作 _ 布景 _ 壁纸 | 1+ | 用于显示选择壁纸的设置。 | | 动作 _ 时区 _ 已更改 | 1+ | 一种受保护的广播意图,只能由系统发送,表示系统时区已更改。额外的EXTRA_TIMEZONE包含在内。 | | 动作 _ 声音 _ 命令 | 1+ | 用于启动语音命令的意图动作。 | | 动作 _ 网页 _ 搜索 | 1+ | 用于启动网络搜索的意图动作。 |

种类

可使用初始化的Intent对象的addCategory方法设置。getCategories方法也可以用于检索接收到的意图的类别。类别提供了关于意图要执行的动作的附加上下文。只有能够促进所有指定类别的活动才能被选择来接收意图。

这里可以看到这样一个例子:

Intent intent = new Intent();
intent.addCategory(Intent.APP_CALCULATOR);

在撰写本文时,Android 文档 2 列出了 39 个以上的标准意向类别属性。表 4-2 列出了这些意向类别中最常见的一部分。

表 4-2

意图类别

|

名字

|

API 级别

|

描述

| | --- | --- | --- | | 类别 _ 备选 | 1+ | 用于标识标准活动的替代活动或用户当前正在查看的数据。 | | 类别应用浏览器 | 15+ | 用于打开一个应该可以浏览互联网的活动。它可以与ACTION_MAIN一起使用,以打开首选的浏览器应用。 | | 类别 _ 应用 _ 计算器 | 15+ | 用于打开应该能够执行标准算术运算的活动。它可以和ACTION_MAIN一起使用来打开计算器应用。 | | 类别 _ 应用 _ 日历 | 15+ | 用于打开应该能够执行标准日历操作的活动。它可以和ACTION_MAIN一起使用来打开日历应用。 | | 类别 _ 应用 _ 联系人 | 15+ | 用于打开应该能够查看和操作地址簿条目的活动。它可以和ACTION_MAIN一起使用,打开通讯录应用。 | | 类别 _ 应用 _ 电子邮件 | 15+ | 用于打开应该能够发送和接收电子邮件的活动。它可以和ACTION_MAIN一起使用来打开电子邮件应用。 | | 类别应用文件 | 15+ | 用于打开应该能够管理存储在设备上的文件的活动。它可以和ACTION_MAIN一起使用来打开文件应用。 | | 类别 _ 应用 _ 图库 | 15+ | 用于打开一个活动,该活动应该能够查看和操作存储在设备上的图像和视频文件。它可以与ACTION_MAIN一起使用,打开图库应用。 | | 类别 _ 应用 _ 地图 | 15+ | 用于打开一个应该能够显示用户当前位置的活动。它可以和ACTION_MAIN一起使用来打开地图应用。 | | 类别 _ 应用 _ 市场 | 11+ | 用于打开允许用户浏览和安装新应用的活动。 | | 类别 _ 应用 _ 消息 | 15+ | 用于打开应该能够发送和接收文本消息的活动。它可以和ACTION_MAIN一起使用来打开消息应用。 | | 类别 _ 应用 _ 音乐 | 15+ | 用于打开应该能够在设备上播放音乐的活动。它可以和ACTION_MAIN一起使用来打开音乐应用。 | | 类别 _ 可浏览 | 1+ | 指示可以从浏览器直接调用和启动的活动(例如,通过用户选择到谷歌 Play 商店网站的链接并转到谷歌 Play 商店应用)。 | | 类别 _ 汽车 _ 模式 | 8+ | 表示该活动已经过优化,适合在汽车环境中工作。 | | 类别 _ 默认 | 1+ | 用于标识可用作默认操作的活动。 | | 类别 _ 主页 | 1+ | 当设备启动时以及每当用户返回到开始的活动时启动的活动。 | | 类别 _ 启动器 | 1+ | 用于标识可在设备上用作初始活动的活动(即主屏幕)。 | | 类别 _ 猴子 | 1+ | 表示可以由 monkey(一个 Android UI fuzzer)或其他自动化工具测试的活动。 |

额外服务

可使用初始化的Intent对象的putExtra方法设置。getExtras方法也可以用于检索接收到的意向的附加内容。extra 是接收者可以使用的捆绑包(一组不同类型的键值对)(例如,当与您的社交媒体应用共享笔记时,笔记文本将作为捆绑包中的字段发送)。这个包可以包含专有密钥或 Android 系统中设置的密钥,如EXTRA_ALARM_COUNT

这里可以看到这样一个例子:

Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_TEXT, "This is an example text extra");

在撰写本文时,Android 文档 3 列出了 80 个以上的标准意图额外属性。表 4-3 列出了这些额外意图中最常见的一部分。

表 4-3

额外意图

|

名字

|

API 级别

|

描述

| | --- | --- | --- | | 额外更改组件名称列表 | 7+ | intent action ACTION_PACKAGE_CHANGED的一部分,包含所有已更改组件的字符串数组。 | | extra _ which _KILL_APP-额外 _ 包括 _ kill _ app | 1+ | 部分ACTION_PACKAGE_REMOVEDACTION_PACKAGE_CHANGED动作并覆盖重启目标应用的默认行为。 | | 额外数据已移除 | 3+ | ACTION_PACKAGE_REMOVED动作的一部分,用于指示移除应该是完全卸载,移除数据和代码,而不是留下数据的部分卸载(后者作为更新的一部分执行)。 | | EXTRA_HTML_TEXT | 16+ | 可以作为ACTION_SEND动作的一部分,与EXTRA_TEXT附加文本一起使用,表示附加文本是 HTML 格式的文本。 | | 额外 MIME 类型 | 19+ | 用于设置可接受的 MIME 类型(例如,audio/mpeg 或 audio/*)。 | | 额外 _ 非 _ 未知 _ 来源 | 14+ | ACTION_INSTALL_PACKAGE动作的一部分,并指示要安装的应用正从发送意图的应用安装,而不是从未知源安装。 | | EXTRA_PACKAGE_NAME | 24+ | 包含应用名称。 | | 额外电话号码 | 1+ | ACTION_NEW_OUTGOING_CALLACTION_CALL动作的一部分,包含呼叫的电话号码。 | | 额外安静模式 | 24+ | 指示静音模式(在这种模式下,配置文件中的所有应用都被终止)是打开还是关闭。 | | EXTRA_REFERRER | 17+ | 在意向发起活动时使用,以确定发起者。值以 URI 的形式提供。 | | 额外替换 | 3+ | 用作ACTION_PACKAGE_REMOVED动作的一部分,表示包已被替换。 | | EXTRA_REFERRER_NAME | 22+ | xtra 的替代产品;但是,该值是作为字符串而不是 URI 提供的。 | | 仅额外关闭用户空间 | 19+ | 用作ACTION_SHUTDOWN动作的一部分,以确定应执行部分关闭。这种部分关闭只是重新启动用户空间,而不是执行完整的操作系统重新启动。 | | 额外 _ 主题 | 1+ | 包含所发送数据主题的高级别附加内容。 | | 额外文本 | 1+ | ACTION_SEND动作的一部分。包含以CharSequence(或String形式接收的数据的高级额外数据,因为字符串实现了CharSequence接口)。 | | 额外时间 | 30+ | 一个高级别 extra,其值包含从 Epoch 开始的以毫秒为单位的时间(例如,1608553466)。 | | EXTRA_TIMEZONE | 30+ | ACTION_TIMEZONE_CHANGED动作的一部分,指示设备的时区。 | | 额外标题 | 1+ | ACTION_CHOOSER的一部分向用户提供一个标题(CharSequenceString等)。). | | EXTRA_UID | 1+ | ACTION_UID_REMOVEDACTION_PACKAGE_REMOVEDACTION_PACKAGE_CHANGED的一部分,用于识别正在运行的应用。 |

旗帜

可使用初始化的意图对象的setFlag方法设置。标志表示正在启动的组件的行为(例如,不包括任务堆栈中的已启动活动)。getFlags方法也可以用于检索接收到的意向标志。

这里可以看到这样一个例子:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

在撰写本文时,Android 文档 4 列出了多达 35 个意向标志。表 4-4 显示了这些意向标志中最常见的一部分。

表 4-4

意图标志

|

名字

|

API 级别

|

描述

| | --- | --- | --- | | 排除 _ 已停止 _ 包 | 12+ | Android 系统的默认行为是在选择要启动的适用组件时考虑设备上的所有应用。如果此标志设置为“已停止”,将不会包括软件包。 | | 标志 _ 活动 _ 否 _ 历史 | 1+ | 如果设置,则在用户离开活动(例如,用户移动到另一个应用)之后,新活动不会保留在历史堆栈中。 | | 标志 _ 活动 _ 先前 _ 是 _ 顶部 | 1+ | 当使用这个标志时,前一个活动保持在任务堆栈的顶部,用于决定新的意图被发送到哪里。 | | FLAG_ACTIVITY_SINGLE_TOP | 1+ | 设置后,如果接收意图的活动已经在任务堆栈的顶部,则不会发生任何操作。 | | 标志 _ 活动 _ 新 _ 任务 | 11+ | 使用此标志可以创建一组新的活动,用户可以使用单独的任务堆栈遍历这些活动。 | | 标志 _ 活动 _ 无 _ 动画 | 5+ | 当设置时,没有过渡动画将被应用于特定活动的开始。 | | 标志 _ 活动 _ 清除 _ 顶部 | 1+ | 如果已设置,并且正在启动的活动已经在当前任务堆栈中,则堆栈中的所有其他活动都将被删除,并由新活动替换。 | | 标记 _ 活动 _ 排除 _ 来自 _ 最近 | 1+ | 如果设置,则启动的活动不包括在最近启动的应用的系统列表中。 |

任务堆栈

也被称为 back stack,这是一个后进先出的堆栈,它存储了一个活动的逻辑列表,供用户遍历。当一个应用启动并且当前不存在它的任务时,应用清单中带有MAIN action的活动被启动。当这个活动来到前台时,它在任务堆栈上创建一个新的根(如图 4-14-2 所示)。启动的每个后续活动都放在任务堆栈中,当用户选择 back 按钮时,这些活动会弹出堆栈。每个应用可以有一个或多个任务堆栈,具体取决于启动活动时设置的特定标志。

img/509502_1_En_4_Fig1_HTML.png

图 4-1

Android 任务堆栈示例

当任务不在前台时(例如,主屏幕按钮已经被按下或者用户遍历到另一个应用/任务堆栈),在堆栈顶部的活动进入onPause()状态并且移动到后台。后台可以同时存在多个任务;但是,如果需要保留内存,系统可能会销毁这些任务。

还需要注意的是,通过使用服务和其他长期运行的实用程序,应用可以在后台运行,而不会出现在任务堆栈中;这将在第 9 章中进一步描述。

img/509502_1_En_4_Fig2_HTML.jpg

图 4-2

在用户界面中查看多个任务堆栈

意图过滤器

所有组件都应该有一个android:exported tag。默认情况下,这是 false,如果是 false,那么组件只能从其应用内部接收消息。通过添加任何意图过滤器,这自动为真,并且意味着外部应用可以与组件交互。当系统接收到一个隐含的意图时,这些意图过滤器也用于通告组件,并决定哪些应用可以接收该意图。有三种主要类型的意图过滤器,它们是ActionDataCategory(如前所述)。

下面是一个带有MAIN动作和LAUNCHER类别集的应用的典型入口点的例子。主操作定义该组件应该作为应用的入口点启动,并且它不接收任何数据。启动器类别定义了活动应该显示在顶层启动器中。

应用入口点活动的示例意图过滤器:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

开始内部活动,数据:

public void sendIntentToActivityInApp(){
    Intent intent= new Intent(this, IntentReceiver.class);
    // Update class to be internal class (to the application) to receive the intent
    Bundle bundle= new Bundle();
    bundle.putString("key", "value");
    intent.putExtras(bundle);
    startActivity(intent);
}

在活动中接收意向捆绑:

protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getIntent().hasExtra("key")) {
      String value = getIntent().getExtras().getString("key");
      Log.v(this.getClass().getSimpleName(), value);
    }
}

启动外部应用:

public void startActivityViaIntent(){
    Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.android.chrome");
    startActivity(launchIntent);
}

启动外部应用的特定活动:

public void sendIntentToAnotherActivity(){
    Intent intent = new Intent();
    intent.setClassName("com.android.chrome", "com.google.android.apps.chrome.Main");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ;

    startActivity(intent);
}

向另一个活动发送文件。从 API 级别 24 开始,向其他应用发送文件需要一个文件提供程序,这样就可以共享自定义的 URI:

public void sendFileToAnoutherApplication(){
     File file =new File(getApplicationContext().getFilesDir(),"/text/test.txt");
     writeFileInInternalStorage(file, "Hello World");

     Intent intent = new Intent(Intent.ACTION_SEND);

     Uri contentUri = getUriForFile(getApplicationContext(), "com.example.intents.fileprovider", file);
     intent.setType("text/plain");

     intent.putExtra(Intent.EXTRA_STREAM,contentUri);

     startActivity(Intent.createChooser(intent , "File path"));
 }

隐含电话号码意图:

public void phoneNumberIntent(){
    Uri number = Uri.parse("tel:5551234");
    Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
    startActivity(callIntent);
}

隐含的 URL 意图:

public void webIntent(){
    Uri webpage = Uri.parse("https://www.android.com");
    Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
    startActivity(webIntent);
}
Footnotes [1](#Fn1_source) “意向| Android 开源项目。”2020 年 11 月 30 日, [`https://developer.android.com/reference/android/content/Intent#flags`](https://developer.android.com/reference/android/content/Intent%2523flags) 。于 2020 年 12 月 26 日访问。   [2](#Fn2_source) “意向| Android 开源项目。”2020 年 11 月 30 日, [`https://developer.android.com/reference/android/content/Intent#flags`](https://developer.android.com/reference/android/content/Intent%2523flags) 。于 2020 年 12 月 26 日访问。   [3](#Fn3_source) “意向| Android 开源项目。”2020 年 11 月 30 日, [`https://developer.android.com/reference/android/content/Intent#flags`](https://developer.android.com/reference/android/content/Intent%2523flags) 。于 2020 年 12 月 26 日访问。   [4](#Fn4_source) “意向| Android 开源项目。”2020 年 11 月 30 日, [`https://developer.android.com/reference/android/content/Intent#flags`](https://developer.android.com/reference/android/content/Intent%2523flags) 。于 2020 年 12 月 26 日访问。