Android 四大组件桥梁 —— Intent (意图) 详解
目录
一、Intent 概述
二、Intent 的类型(显式和隐式)
1.显式 Intent
2.隐式 Intent
三、Intent 的核心组成部分
1. Action(动作)
2. Data(数据)
3.Type(数据类型)
4.Category(类别)
5.Extras(附加数据)
6.Flags(标志)
四、Intent Filter(意图过滤器)
五、Intent传递简单数据
1.使用 putExtra() 方法逐个添加
2. 使用 Bundle 对象打包数据
3. 传递数组和集合
六、使用 Intent 传递复杂数据
1.传递 Parcelable 对象(性能更高)
2.传递 Serializable 对象(更简单,但性能较差)
七、获取 Activity 的结果:startActivityForResult
一、Intent 概述
Intent(意图)是 Android 程序中在不同组件之间(如 Activity、Service、BroadcastReceiver)传递消息的对象。它可以在运行时绑定不同的组件,是组件间通信的核心。
主要作用:
-
启动组件:启动一个 Activity、Service 或发送一个 Broadcast。
-
传递数据:在组件之间携带数据。
-
声明目标组件:可以明确指定要启动的组件,也可以只描述要执行的动作,由系统来选择合适的组件。
- startActivity(Intent)/startActivityForResult(Intent):来启动一个Activity
- startService(Intent)/bindService(Intent):来启动一个Service
- sendBroadcast:发送广播到指定BroadcastReceiver
二、Intent 的类型(显式和隐式)
1.显式 Intent
- 定义:明确指定了要启动的组件类名(
Activity.class
,Service.class
)。通常用于启动应用内的组件,因为你知道具体的类名。 - 核心属性:
ComponentName
。
// 启动同一个应用内的 AnotherActivity
Intent explicitIntent = new Intent(MainActivity.this, AnotherActivity.class);
startActivity(explicitIntent);// 或者通过 setComponent 方法
Intent intent = new Intent();
intent.setComponent(new ComponentName(MainActivity.this, "com.example.app.AnotherActivity"));
startActivity(intent);// 启动一个 Service
Intent serviceIntent = new Intent(MainActivity.this, MyService.class);
startService(serviceIntent);
- 系统处理: 系统不进行解析,直接启动指定的组件。如果该组件存在且声明在Manifest中,则启动;否则抛出
ActivityNotFoundException
或类似异常。
2.隐式 Intent
- 定义:不指定具体的组件类名,而是声明一个要执行的动作(Action),并附加一些数据(Data)、类别(Category)等信息。系统会根据这些信息在所有应用的 AndroidManifest.xml 中注册的
<intent-filter>
进行匹配,找到并启动最适合的组件。 - 核心属性:
Action
,Data
,Category
,Type
,Extras
(用于传额外数据)等。
// 打开一个网页
Intent implicitIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"));
startActivity(implicitIntent);// 发送邮件
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:contact@example.com"));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "邮件主题");
startActivity(emailIntent);// 分享文本
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "这是要分享的文本");
startActivity(Intent.createChooser(shareIntent, "分享到"));
- 系统处理:
- 查询PackageManager: 系统向
PackageManager
查询所有已安装应用的AndroidManifest.xml
文件。- 匹配
<intent-filter>
: 对于每个组件(Activity/Service/BroadcastReceiver)声明的<intent-filter>
,系统尝试匹配Intent的内容:
- Action: Intent的Action必须在filter声明的
<action>
列表中。- Data: Intent的Data (
Uri
&MIME Type
) 必须匹配filter声明的<data>
元素。Uri
匹配Scheme, Host, Port, Path;Type
匹配或*/*
通配。- Category: Intent携带的所有Category必须在filter声明的
<category>
列表中(filter可以声明额外的Category)。CATEGORY_DEFAULT
通常需要显式声明在filter中。- 过滤结果: 所有满足上述条件的组件被筛选出来。
- 选择目标:
- Activity/Service: 如果有多个匹配项,系统通常会显示一个选择器对话框(Resolver Activity) 让用户选择(除非设置了
Intent.createChooser()
强制显示,或设置了Intent.FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK
等特殊标志,或目标应用设置了android:autoVerify="true"
并通过了App Links验证)。- BroadcastReceiver: 所有匹配的Receiver都会收到广播(有序广播按优先级顺序)。
三、Intent 的核心组成部分
一个 Intent 对象包含了许多信息,系统根据这些信息来决定哪个组件应该被启动。
1. Action(动作)
一个字符串,表示要执行的通用动作。例如:
-
Intent.ACTION_VIEW
: 显示数据给用户。 -
Intent.ACTION_EDIT
: 编辑数据。 -
Intent.ACTION_DIAL
: 拨号。 -
Intent.ACTION_SEND
: 发送数据(分享)。 -
Intent.CUSTOM_ACTION
: 也可以使用自定义的 Action 字符串。例如com.google.app.myapp.CUSTOM_ACTION,
2. Data(数据)
数据通常与 Action 配合使用,用 Uri
对象表示要操作的数据。
-
ACTION_VIEW
+content://contacts/people/1
: 显示 ID 为 1 的联系人信息。 -
ACTION_EDIT
+file://sdcard/example.txt
: 编辑 SD 卡上的文本文件。 -
ACTION_CALL
+tel:123456
: 直接呼叫号码(需要CALL_PHONE
权限)。
3.Type(数据类型)
指定 Data 所指向数据的 MIME 类型。如果设置了 Type,系统通常会忽略 Data 中的 URI 的 MIME 类型推断。
intent.setType("image/png");
// 注意:setData 和 setType 会互相清除对方,所以如果要同时设置,请使用 setDataAndType
intent.setDataAndType(imageUri, "image/png");
4.Category(类别)
一个字符串,提供了关于目标组件的额外信息。一个 Intent 可以添加多个 Category。
-
Intent.CATEGORY_LAUNCHER
: 表示该 Activity 是任务的初始 Activity,会出现在应用启动器中。 -
Intent.CATEGORY_DEFAULT
: 如果希望组件能够被隐式 Intent 启动,通常需要声明这个 Category。 -
Intent.CATEGORY_BROWSABLE
: 表示目标 Activity 允许本身被浏览器启动。
5.Extras(附加数据)
以键值对形式存放的额外信息,用于在组件间传递数据。使用 putExtra()
方法添加。
-
系统定义了很多
EXTRA_*
常量,如Intent.EXTRA_EMAIL
,Intent.EXTRA_STREAM
。 -
也可以使用自定义的键。
// 传递数据
intent.putExtra("KEY_NAME", "Alice");
intent.putExtra("KEY_AGE", 25);// 在目标 Activity 中获取
String name = getIntent().getStringExtra("KEY_NAME");
int age = getIntent().getIntExtra("KEY_AGE", 0); // 0 是默认值
6.Flags(标志)
用于指示 Android 系统如何启动 Activity(例如,Activity 应属于哪个任务)以及启动之后如何对待它。这个后面说 Activity 的时候会详细讲解。
-
Intent.FLAG_ACTIVITY_NEW_TASK
: 在一个新任务中启动 Activity。 -
Intent.FLAG_ACTIVITY_CLEAR_TOP
: 如果目标 Activity 已在当前任务中运行,则不会启动该 Activity 的新实例,而是会清除其上的所有其他 Activity,并将其置于任务顶部。 -
Intent.FLAG_ACTIVITY_SINGLE_TOP
: 如果要启动的 Activity 是当前 Activity(位于返回栈的顶部),则不会创建新实例。
四、Intent Filter(意图过滤器)
为了让一个组件(如 Activity)能够响应特定的隐式 Intent,必须在 AndroidManifest.xml
中为该组件声明 <intent-filter>
。
一个 <intent-filter>
可以定义:
-
<action>
: 至少一个,声明组件能响应的动作。 -
<category>
: 零个或多个,声明组件所属的类别。如果没有指定类别,则只响应没有类别的 Intent。但CATEGORY_DEFAULT
几乎总是需要。 -
<data>
: 零个或多个,声明组件能处理的数据类型和 URI 模式。
示例:一个 Activity 声明自己可以处理“分享”动作和文本类型。
<activity android:name=".ShareActivity"><intent-filter><!-- 指定动作 --><action android:name="android.intent.action.SEND" /><!-- 指定类别,使其能被隐式 Intent 启动 --><category android:name="android.intent.category.DEFAULT" /><!-- 指定数据类型 --><data android:mimeType="text/plain" /></intent-filter>
</activity>
五、Intent传递简单数据
1.使用 putExtra() 方法逐个添加
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.putExtra("name", "张三");
intent.putExtra("age", 25);
intent.putExtra("score", 95.5f);
intent.putExtra("isStudent", true);
startActivity(intent);// 接收数据
Intent intent = getIntent();
String name = intent.getStringExtra("name");
int age = intent.getIntExtra("age", 0); // 第二个参数为默认值
float score = intent.getFloatExtra("score", 0.0f);
boolean isStudent = intent.getBooleanExtra("isStudent", false);
2. 使用 Bundle 对象打包数据
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "李四");
bundle.putInt("age", 30);
bundle.putDouble("salary", 8000.50);
bundle.putBoolean("isEmployed", true);
intent.putExtras(bundle);
startActivity(intent);// 接收数据
Bundle bundle = getIntent().getExtras();
if (bundle != null) {String name = bundle.getString("name");int age = bundle.getInt("age");double salary = bundle.getDouble("salary");boolean isEmployed = bundle.getBoolean("isEmployed");
}
3. 传递数组和集合
// 发送数据
Intent intent = new Intent(MainActivity.this, SecondActivity.class);// 基本类型数组
intent.putExtra("intArray", new int[]{1, 2, 3, 4, 5});
intent.putExtra("stringArray", new String[]{"A", "B", "C"});// ArrayList
ArrayList<String> list = new ArrayList<>();
list.add("Java");
list.add("Kotlin");
list.add("Dart");
intent.putStringArrayListExtra("languageList", list);startActivity(intent);// 接收数据
int[] intArray = getIntent().getIntArrayExtra("intArray");
String[] stringArray = getIntent().getStringArrayExtra("stringArray");
ArrayList<String> languageList = getIntent().getStringArrayListExtra("languageList");
六、使用 Intent 传递复杂数据
除了基本类型,还可以通过 Intent 传递可序列化(Serializable)或可打包(Parcelable)的对象。类似bitmap默认实现Parcelable接口,直接传递即可
1.传递 Parcelable 对象(性能更高)
-
让你的类实现
Parcelable
接口。 -
实现
describeContents()
和writeToParcel(Parcel dest, int flags)
方法。 -
创建一个名为
CREATOR
的静态字段。
public class User implements Parcelable {private String name;private int age;// ... 构造方法、getter/setter ...protected User(Parcel in) {name = in.readString();age = in.readInt();}public static final Creator<User> CREATOR = new Creator<User>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}@Overridepublic User[] newArray(int size) {return new User[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(age);}
}
4.传递和接收:
// 发送方
User user = new User("Bob", 30);
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("user_key", user);
startActivity(intent);// 接收方
User receivedUser = getIntent().getParcelableExtra("user_key");
通过writeToParcel将你的对象映射成Parcel对象,再通过createFromParcel将Parcel对象映射 成你的对象。也可以将Parcel看成是一个流,通过writeToParcel把对象写到流里面, 在通过createFromParcel从流里读取对象,只不过这个过程需要你来实现,因此写的 顺序和读的顺序必须一致。
2.传递 Serializable 对象(更简单,但性能较差)
// 让类实现 Serializable 接口
public class User implements Serializable {// ...
}// 传递
intent.putExtra("user_key", user);// 接收
User receivedUser = (User) getIntent().getSerializableExtra("user_key");
两种序列化方式的比较:
- 1)在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
- 2)Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
- 3)Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的 持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable。
七、获取 Activity 的结果:startActivityForResult
(注意:此方法已被标记为 deprecated
,推荐使用新的 Activity Result API,但理解其原理仍有价值)
旧方式:
// 启动 Activity,并期待返回结果
private static final int REQUEST_CODE_PICK_CONTACT = 1;Intent pickContactIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(pickContactIntent, REQUEST_CODE_PICK_CONTACT);// 在源 Activity 中重写 onActivityResult 来接收结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE_PICK_CONTACT) {if (resultCode == RESULT_OK) {// 处理返回的数据 Intent ‘data’Uri contactUri = data.getData();// ... 处理联系人 URI ...}}
}
新方式(Activity Result API):
-
注册一个 Activity Result Launcher。
-
启动它。
// 在 Activity 或 Fragment 中
private ActivityResultLauncher<Intent> someActivityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(),result -> {// 这里是回调if (result.getResultCode() == Activity.RESULT_OK) {Intent data = result.getData();// 处理返回的 Intent data}});// 当需要启动 Activity 并获取结果时
Intent intent = new Intent(this, TargetActivity.class);
someActivityResultLauncher.launch(intent);