Android - Intent/过滤器
Android - Intent 与过滤器
Section titled “Android - Intent 与过滤器”在 Android 中,Intent 是一种消息传递对象,可用于从另一个应用组件请求操作。Intent 以多种方式促进组件间通信,例如启动 Activity、启动 Service 或发送广播。
Intent 本身是一种被动数据结构,包含要执行的操作或(对于广播而言)已发生的事件的抽象描述。
例如,如果您的 Activity 需要发送电子邮件,它会创建一个带有 ACTION_SENDTO action 和 mailto: URI 的 Intent。然后它将此 Intent 传递给 startActivity()。Android 系统的 Intent 解析器会找到一个合适的电子邮件应用来处理此请求,如果多个应用可以处理,则会呈现一个选择器。
// 示例:发送电子邮件Intent emailIntent = new Intent(Intent.ACTION_SENDTO);emailIntent.setData(Uri.parse("mailto:")); // 只有电子邮件应用应该处理此 IntentemailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"recipient@example.com"});emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email Subject");emailIntent.putExtra(Intent.EXTRA_TEXT, "Email body content.");
if (emailIntent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(emailIntent, "Choose an email client:"));} else { // 处理没有电子邮件客户端可用的情况 Toast.makeText(this, "No email client installed.", Toast.LENGTH_SHORT).show();}类似地,要打开网页,您可以使用 ACTION_VIEW 和 HTTP/HTTPS URI:
// 示例:打开网页Uri webpage = Uri.parse("https://www.example.com");Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
if (webIntent.resolveActivity(getPackageManager()) != null) { startActivity(webIntent);} else { // 处理没有浏览器可用的情况 Toast.makeText(this, "No web browser found.", Toast.LENGTH_SHORT).show();}Intent 通过特定方法传递给不同的组件类型:
| 序号 | 方法及描述 |
|---|---|
| 1 | Context.startActivity(Intent intent) 启动新的 Activity 或让现有 Activity 执行新操作。 Intent 描述要启动的 Activity 并携带任何必要的数据。 |
| 2 | Context.startService(Intent intent) / Context.bindService(Intent intent, …)startService() 启动 Service 或向正在运行的 Service 发送新指令。bindService() 建立与 Service 的连接以便直接交互。 |
| 3 | Context.sendBroadcast(Intent intent) 将 Intent 作为广播消息发送给所有相关的 BroadcastReceiver 组件。 |
Intent 对象
Section titled “Intent 对象”Intent 对象携带 Android 系统用于确定要启动哪个组件的信息,以及接收组件用于执行操作的信息。Intent 对象的主要组成部分包括:
组件名称(用于显式 Intent)
Section titled “组件名称(用于显式 Intent)”要启动的组件的名称(例如 com.example.app.MyActivity)。如果设置了此项,则 Intent 将直接传递给此组件实例。这称为显式 Intent。如果未设置,Android 将使用 Intent 的其他信息来查找合适的目标(隐式 Intent)。通过 setComponent()、setClass() 或 setClassName() 设置。
Action
Section titled “Action”指定要执行的通用操作的字符串(例如 Intent.ACTION_VIEW、Intent.ACTION_SEND)。对于广播 Intent,这是已发生并正在报告的操作(例如 Intent.ACTION_BOOT_COMPLETED)。Intent 类定义了许多 ACTION_* 常量。这是隐式 Intent 的关键部分。通过 setAction() 设置。
一个 Uri(统一资源标识符),引用要对其执行操作的数据和/或该数据的 MIME 类型。例如,如果 action 是 ACTION_VIEW,则 data 字段可能包含联系人数据库中某人的 URI 或网页的 HTTP URI。通过 setData()、setType() 或 setDataAndType() 设置。
Action/Data 对示例:
| 序号 | Action/Data 对及描述 |
|---|---|
| 1 | ACTION_VIEW, content://contacts/people/1显示 ID 为 “1” 的个人的信息。 |
| 2 | ACTION_DIAL, tel:1234567890显示电话拨号器,并填充给定的号码。 |
| 3 | ACTION_VIEW, geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California显示地图,显示指定位置。 |
| 4 | ACTION_EDIT, content://contacts/people/1编辑 ID 为 “1” 的个人的信息。 |
| 5 | ACTION_SENDTO, mailto:user@example.com打开电子邮件编辑器,收件人地址为 user@example.com。 |
| 6 | ACTION_SEND (with EXTRA_TEXT and setType("text/plain"))分享纯文本内容。 |
Category
Section titled “Category”包含有关应处理 Intent 的组件类型的附加信息的可选字符串。常见的 Category 包括 Intent.CATEGORY_LAUNCHER(将 Activity 标记为应用的初始入口点)和 Intent.CATEGORY_DEFAULT(如果某个 Activity 是默认选项,则允许隐式 Intent 启动它)。使用 addCategory() 添加。
Extras
Section titled “Extras”携带完成请求操作所需的附加信息的键值对。存储在 Bundle 对象中。例如,ACTION_SEND Intent 可能包含 Intent.EXTRA_EMAIL(电子邮件地址的字符串数组)和 Intent.EXTRA_SUBJECT(主题字符串)。使用 putExtra() 方法添加。
修改 Intent 行为的可选整数,例如应如何启动 Activity(例如 FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP)或系统在启动后应如何处理它。使用 addFlags() 添加。
| 序号 | Flag 及描述 |
|---|---|
| 1 | FLAG_ACTIVITY_NEW_TASK 如果设置,此 Activity 将成为此历史堆栈上新任务的开始。通常用于从现有 Activity Context 之外启动 Activity 时(例如,从 Service 或 BroadcastReceiver)。 |
| 2 | FLAG_ACTIVITY_CLEAR_TOP 如果正在启动的 Activity 已在当前任务中运行,则不会启动新的实例,而是关闭其之上的所有其他 Activity,并通过 onNewIntent() 将此 Intent 作为新 Intent 发送给(现在位于顶部的)旧 Activity。 |
| 3 | FLAG_ACTIVITY_SINGLE_TOP 如果正在启动的 Activity 已位于当前任务的顶部,则不会启动新的实例,而是现有实例将在其 onNewIntent() 方法中接收 Intent。 |
Intent 的类型
Section titled “Intent 的类型”Intent 主要有两种类型:
显式 Intent
Section titled “显式 Intent”通过其完全限定类名指定要启动的组件。您通常使用显式 Intent 来启动自己应用内的组件,因为您知道要启动的 Activity 或 Service 的类名。
// 显式 Intent:从 FirstActivity 启动 SecondActivity(都在 com.example.app 中)Intent explicitIntent = new Intent(FirstActivity.this, SecondActivity.class);// 可选:向 Intent 添加数据explicitIntent.putExtra("MESSAGE_KEY", "Hello from FirstActivity!");startActivity(explicitIntent);在 SecondActivity 中,您可以检索附加数据:
// 在 SecondActivity.java 的 onCreate() 或 onNewIntent() 中Bundle extras = getIntent().getExtras();if (extras != null) { String message = extras.getString("MESSAGE_KEY"); // 使用消息}隐式 Intent
Section titled “隐式 Intent”不指定特定的组件。相反,它们声明要执行的通用操作,这允许来自另一个应用的组件处理它。例如,如果您想在地图上向用户显示某个位置,可以使用隐式 Intent 请求另一个能够处理此操作的应用显示指定位置。
// 隐式 Intent:查看联系人Intent implicitIntent = new Intent(Intent.ACTION_VIEW);implicitIntent.setData(ContactsContract.Contacts.CONTENT_URI);if (implicitIntent.resolveActivity(getPackageManager()) != null) { startActivity(implicitIntent);} else { Toast.makeText(this, "No app can handle this action.", Toast.LENGTH_SHORT).show();}当您使用隐式 Intent 时,Android 系统会通过将 Intent 的内容与设备上其他应用的 Manifest 文件中声明的 Intent 过滤器进行比较,来查找合适的组件启动。如果多个组件匹配,系统可能会显示一个对话框供用户选择要使用的应用(一个“应用选择器”)。
示例:使用 Intent 启动系统应用
Section titled “示例:使用 Intent 启动系统应用”此示例展示了如何使用隐式 Intent 启动网页浏览器和电话拨号器。
-
创建一个新的 Android Studio 项目(例如
com.example.intentdemo)。 -
修改
res/layout/activity_main.xml以包含两个按钮:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" android:padding="16dp" tools:context=".MainActivity">
<Button android:id="@+id/button_start_browser" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Open Web Browser" />
<Button android:id="@+id/button_start_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:text="Open Phone Dialer" />
</LinearLayout>- 修改
MainActivity.java(或.kt)以处理按钮点击(使用 View Binding 进行现代实践):
// MainActivity.javapackage com.example.intentdemo;
import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.widget.Toast;import com.example.intentdemo.databinding.ActivityMainBinding; // 生成的 ViewBinding 类
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = ActivityMainBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot());
binding.buttonStartBrowser.setOnClickListener(v -> { Uri webpage = Uri.parse("https://developer.android.com"); Intent intent = new Intent(Intent.ACTION_VIEW, webpage); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } else { Toast.makeText(MainActivity.this, "No browser found", Toast.LENGTH_SHORT).show(); } });
binding.buttonStartPhone.setOnClickListener(v -> { Uri phoneNumber = Uri.parse("tel:1234567890"); // 示例号码 Intent intent = new Intent(Intent.ACTION_DIAL, phoneNumber); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); } else { Toast.makeText(MainActivity.this, "No phone app found", Toast.LENGTH_SHORT).show(); } }); }}-
确保在您的模块的
build.gradle或build.gradle.kts文件中启用 View Binding:android { buildFeatures { viewBinding = true } } -
运行应用。点击“打开网页浏览器”将启动浏览器并导航到指定 URL。点击“打开电话拨号器”将打开拨号器应用并预填充号码。
resolveActivity()检查很重要,可以防止在没有应用可以处理 Intent 时发生崩溃。
Intent 过滤器
Section titled “Intent 过滤器”Intent 过滤器是应用 Manifest 文件 (AndroidManifest.xml) 中的一个表达式,指定组件(Activity、Service 或 BroadcastReceiver)可以接收的 Intent 类型。当您创建隐式 Intent 时,Android 系统会搜索具有与该 Intent 匹配的 Intent 过滤器的组件。
一个 <intent-filter> 元素声明其父组件的能力。它可以包含一个或多个以下子元素:
<action android:name="...">:声明它接受的 Intent Action。必须至少包含一个 Action。<category android:name="...">:声明它接受的 Intent Category。Intent 必须匹配过滤器中的所有 Category。<data ...>:声明它接受的数据类型,由 URI 方面(scheme、host、port、path)和/或 MIME 类型指定。
示例:声明用于处理分享纯文本的 Activity (ShareActivity):
<activity android:name=".ShareActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter></activity>现在可以通过以下隐式 Intent 启动此 ShareActivity:
Intent sendIntent = new Intent();sendIntent.setAction(Intent.ACTION_SEND);sendIntent.putExtra(Intent.EXTRA_TEXT, "This is my text to send.");sendIntent.setType("text/plain");
// 验证 Intent 是否能解析到 Activityif (sendIntent.resolveActivity(getPackageManager()) != null) { startActivity(Intent.createChooser(sendIntent, "Share via"));}CATEGORY_DEFAULT 很重要,因为它允许在使用隐式 Intent 调用 startActivity() 时找到该 Activity。如果没有它,该 Activity 只能通过显式 Intent 启动。
Intent 解析过程: 使用隐式 Intent 时,系统会将 Intent 的 Action、Data(URI 和 MIME 类型)和 Category 与所有已安装应用的 <intent-filter> 声明进行比较。如果找到匹配项(或多个匹配项),相应的组件可以处理该 Intent。如果没有组件可以处理该 Intent,则调用 startActivity() 将导致 ActivityNotFoundException。在通过隐式 Intent 启动 Activity 之前,务必使用 resolveActivity(getPackageManager()) != null 进行检查,或者使用 try-catch 块。
Intent 和 Intent 过滤器是 Android 应用互操作性的基石,可以在单个应用内和不同应用之间实现强大而灵活的组件通信。