Android Intent详解
在Android开发中,Intent 是组件间通信的核心机制,用于在Activity、Service、BroadcastReceiver等组件之间传递消息、启动组件或共享数据。它相当于一个“意图”载体,描述了操作的目的、数据和附加信息,是Android组件解耦的关键。
一、Intent 核心作用
- 启动组件:启动Activity(
startActivity())、启动Service(startService()/bindService())、发送广播(sendBroadcast())。 - 传递数据:在组件间传递基本类型、对象、集合等数据。
- 指定操作:通过“动作”“数据”等描述要执行的操作(如“打开网页”“发送邮件”),实现跨应用交互。
二、Intent 类型
根据目标组件的指定方式,Intent分为显式Intent和隐式Intent,适用场景不同。
1. 显式 Intent(Explicit Intent)
特点:直接指定目标组件的完整类名(ComponentName),明确告诉系统要启动哪个组件。
适用场景:启动应用内部组件(如本应用的Activity、Service),因为开发者明确知道目标组件的类名。
示例:启动应用内的SecondActivity
// Kotlin
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)
// Java
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
进阶:通过ComponentName指定
如需动态指定组件(如跨进程启动已知类名的组件),可通过ComponentName构造:
val component = ComponentName(packageName, "com.example.app.SecondActivity")
val intent = Intent().setComponent(component)
startActivity(intent)
2. 隐式 Intent(Implicit Intent)
特点:不直接指定目标组件,而是通过“动作(Action)”“类别(Category)”“数据(Data)”等描述操作意图,由系统根据这些信息匹配最合适的组件(如系统浏览器、邮件应用)。
适用场景:启动跨应用组件(如调用系统浏览器打开网页、调用相机拍照),无需知道目标组件的类名。
示例:打开指定网页(调用系统浏览器)
val intent = Intent(Intent.ACTION_VIEW) // 动作:查看内容
intent.data = Uri.parse("https://www.example.com") // 数据:网页地址
startActivity(intent)
系统会匹配所有声明了ACTION_VIEW且支持http协议的组件(通常是浏览器),并让用户选择打开方式。
三、Intent 核心属性
Intent通过一系列属性描述操作意图,核心属性包括Action、Category、Data、Type、Extra、Flag等。
1. Action(动作)
一个字符串,描述要执行的操作(如“查看”“发送”“编辑”)。系统预定义了大量Action,也可自定义。
常用系统Action:
| Action常量 | 含义 | 适用场景 |
|---|---|---|
Intent.ACTION_VIEW | 查看数据 | 打开网页、图片、文档等 |
Intent.ACTION_SEND | 发送数据 | 分享文本、图片(调用分享面板) |
Intent.ACTION_CALL | 拨打电话 | 需要CALL_PHONE权限 |
Intent.ACTION_EDIT | 编辑数据 | 编辑联系人、图片等 |
Intent.ACTION_MAIN | 应用入口 | launcher启动应用的主Activity |
自定义Action:
需使用全限定名(避免冲突),如com.example.app.ACTION_CUSTOM,用于应用内组件通信或跨应用约定。
2. Category(类别)
对Action的补充,描述组件的额外特性(如“默认组件”“ launcher入口”)。一个Intent可包含多个Category,系统会匹配所有声明的类别。
常用系统Category:
Intent.CATEGORY_DEFAULT:默认类别,隐式启动Activity时必须包含(否则匹配失败)。Intent.CATEGORY_LAUNCHER:标记应用的主入口(与ACTION_MAIN配合,显示在 launcher 上)。
示例:隐式启动时添加默认类别
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://www.example.com")
intent.addCategory(Intent.CATEGORY_DEFAULT) // 必须添加,否则可能匹配失败
startActivity(intent)
3. Data 与 Type(数据与类型)
Data:通过Uri指定操作的数据(如content://contacts/people/1表示某个联系人,http://example.com表示网页)。Type:指定数据的MIME类型(如image/jpeg、text/plain),若Data已包含类型信息(如http对应text/html),Type可省略。
注意:Data和Type会相互覆盖,若需同时设置,需用setDataAndType()而非分别调用setData()和setType():
// 正确:同时设置Data和Type
intent.setDataAndType(Uri.parse("content://media/external/images/1"), "image/jpeg")// 错误:setType()会覆盖Data的类型信息
intent.data = Uri.parse("content://media/external/images/1")
intent.type = "image/jpeg" // 此操作会清除data
4. Extra(附加数据)
键值对形式的附加数据,用于在组件间传递数据(如传递用户ID、文本内容)。支持基本类型、数组、集合,以及实现Serializable或Parcelable的对象。
存储与获取数据:
// 存储数据
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("userId", 123) // 基本类型
intent.putExtra("username", "张三") // 字符串
intent.putExtra("isVip", true) // 布尔值// 传递对象(需实现Parcelable或Serializable)
val user = User("张三", 20) // 假设User实现了Parcelable
intent.putExtra("user", user)startActivity(intent)
// 目标组件中获取数据
val userId = intent.getIntExtra("userId", -1) // 第二个参数为默认值
val username = intent.getStringExtra("username") // 可为null
val isVip = intent.getBooleanExtra("isVip", false)
val user = intent.getParcelableExtra<User>("user") // 需指定泛型
注意:
- 传递大量数据(如大图片)可能导致
TransactionTooLargeException,建议用其他方式(如全局变量、文件)。 Parcelable比Serializable效率更高(Android推荐用Parcelable)。
5. Flag(标记)
通过addFlags()设置,用于控制组件的启动模式、任务栈(Task)行为等。
常用Flag:
| Flag | 作用 |
|---|---|
Intent.FLAG_ACTIVITY_NEW_TASK | 启动新任务栈(常用于Service中启动Activity) |
Intent.FLAG_ACTIVITY_CLEAR_TOP | 若目标Activity已在栈中,清除其上方所有Activity |
Intent.FLAG_ACTIVITY_SINGLE_TOP | 若目标Activity在栈顶,则复用,不创建新实例 |
Intent.FLAG_GRANT_READ_URI_PERMISSION | 授予Uri的读权限(跨应用访问文件时需用) |
示例:启动新任务栈中的Activity(如从Service启动)
val intent = Intent(this, NotificationActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) // 必须添加,否则Service中启动会崩溃
startActivity(intent)
四、Intent 过滤器(Intent Filter)
隐式Intent需要通过Intent过滤器匹配目标组件。过滤器在AndroidManifest.xml中声明,用于告诉系统“该组件能处理哪些Intent”。
一个组件可声明多个过滤器,只要Intent匹配其中一个即可启动该组件。
过滤器匹配规则
Intent与过滤器的匹配需满足3个条件(全部匹配):
- Action匹配:Intent的Action必须在过滤器的
action列表中(或过滤器无Action,Intent也无Action)。 - Category匹配:Intent的所有Category必须在过滤器的
category列表中(过滤器至少包含CATEGORY_DEFAULT,否则隐式启动失败)。 - Data/Type匹配:Intent的Data/Type需与过滤器的
data规则匹配(如协议、MIME类型)。
示例:为Activity配置过滤器
声明一个能处理“查看图片”和“编辑图片”的Activity:
<activity android:name=".ImageHandlerActivity"><intent-filter><!-- Action:支持查看和编辑图片 --><action android:name="android.intent.action.VIEW" /><action android:name="android.intent.action.EDIT" /><!-- Category:必须包含默认类别 --><category android:name="android.intent.category.DEFAULT" /><!-- Data:支持图片类型(image/*),协议不限 --><data android:mimeType="image/*" /></intent-filter>
</activity>
当其他应用发送包含ACTION_VIEW、CATEGORY_DEFAULT、image/jpeg类型的Intent时,系统会将该Activity列为可选目标。
五、特殊用法
1. 启动组件并获取返回结果
通过Activity Result API(替代旧版startActivityForResult())启动Activity并获取返回数据。
示例:启动相机拍照并获取照片
// 1. 注册结果回调
val takePictureLauncher = registerForActivityResult(ActivityResultContracts.TakePicture()) { isSuccess ->if (isSuccess) {// 拍照成功,处理照片(photoUri为拍照后保存的路径)imageView.setImageURI(photoUri)}
}// 2. 启动相机(photoUri为提前创建的临时文件路径)
val photoUri = createTempPhotoUri() // 自定义方法:创建保存照片的Uri
takePictureLauncher.launch(photoUri)
2. PendingIntent(延迟Intent)
一种特殊的Intent,用于在未来某个时刻由其他组件(如系统服务)执行。常用于通知(Notification)、闹钟(AlarmManager)、快捷方式等场景。
特点:
- 包含触发时的权限和上下文,即使创建它的应用已退出,仍可正常执行。
- 支持三种操作:启动Activity(
getActivity())、启动Service(getService())、发送广播(getBroadcast())。
示例:通知中添加PendingIntent(点击通知打开Activity)
// 创建打开DetailActivity的Intent
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("id", 100)// 创建PendingIntent(FLAG_IMMUTABLE:不可变,安全性更高)
val pendingIntent = PendingIntent.getActivity(this,0, // 请求码(区分不同PendingIntent)intent,PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)// 构建通知
val notification = NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("新消息").setContentText("点击查看详情").setSmallIcon(R.drawable.ic_notification).setContentIntent(pendingIntent) // 绑定PendingIntent.build()// 显示通知
notificationManager.notify(NOTIFICATION_ID, notification)
3. 发送广播 Intent
通过sendBroadcast()发送广播,由注册了对应过滤器的BroadcastReceiver接收。
示例:发送自定义广播
// 发送广播
val intent = Intent("com.example.app.ACTION_CUSTOM_BROADCAST")
// 限制广播仅本应用接收(Android 8.0+ 推荐)
intent.setPackage(packageName)
sendBroadcast(intent)
<!-- 注册广播接收器(Manifest中) -->
<receiver android:name=".MyReceiver"><intent-filter><action android:name="com.example.app.ACTION_CUSTOM_BROADCAST" /></intent-filter>
</receiver>
// 广播接收器实现
class MyReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {if ("com.example.app.ACTION_CUSTOM_BROADCAST" == intent.action) {// 处理广播}}
}
六、注意事项
-
隐式Intent的安全性:Android 10(API 29)后,隐式启动Activity需指定包名(
setPackage())或通过resolveActivity()检查是否有匹配组件,否则可能抛出异常:val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com")) if (intent.resolveActivity(packageManager) != null) { // 检查是否有匹配组件startActivity(intent) } else {// 提示用户无可用应用 } -
数据大小限制:Intent传递数据通过Binder机制,总大小限制约1MB,超过会导致
TransactionTooLargeException,建议用文件或跨进程通信(IPC)替代。 -
PendingIntent的 FLAG 选择:Android 12(API 31)后,创建PendingIntent需指定
FLAG_IMMUTABLE或FLAG_MUTABLE:FLAG_IMMUTABLE:Intent不可修改(推荐,安全性高)。FLAG_MUTABLE:允许修改Intent(仅在需要动态更新Intent时使用)。
总结
Intent 是Android组件通信的“桥梁”,通过显式/隐式方式连接组件,支持数据传递和操作描述。掌握其核心属性(Action、Category、Data、Extra)、过滤器匹配规则,以及特殊用法(PendingIntent、Activity Result),是实现组件交互和跨应用功能的基础。合理使用Intent能大幅提升应用的灵活性和扩展性。
