1. Intent的本质:Android组件间的通信信使
在Android开发中,Intent就像现实世界里的快递员——它负责在不同组件(Activity、Service、BroadcastReceiver等)之间传递"包裹"。这个包裹里可以装三类东西:
- 目的地地址(显式Intent):明确指定要启动的组件类名,比如
new Intent(this, DetailActivity.class) - 任务描述(隐式Intent):声明要执行的动作类型(ACTION_VIEW/SEND等),系统会匹配能处理该动作的组件
- 附加数据:通过putExtra()方法放入的键值对,比如要传递的用户ID或图片URI
举个实际场景:当用户点击相册应用中的分享按钮时,系统会创建一个ACTION_SEND类型的Intent,里面携带图片URI。这个Intent就像一封写明"请帮我分享这张照片"的信件,被投递到所有声明能处理分享操作的组件(微信、邮件等应用)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Intent的两种工作模式解析
2.1 显式Intent:精准直达的专车服务
显式Intent需要明确指定目标组件的完整类名,就像叫专车时直接输入司机手机号。典型用法:
java复制Intent explicitIntent = new Intent();
explicitIntent.setClassName("com.example.app", "com.example.app.DetailActivity");
explicitIntent.putExtra("item_id", 12345);
startActivity(explicitIntent);
这种方式的优点是:
- 启动速度最快(无需系统匹配)
- 安全性高(只有你知道的目标会接收)
- 适合应用内部组件跳转
我在实际项目中发现,显式Intent在跨进程通信时需要特别注意:如果目标Activity位于不同应用,必须确保该Activity在AndroidManifest.xml中exported属性为true,否则会抛出SecurityException。
2.2 隐式Intent:发布需求的招标模式
隐式Intent不指定具体组件,而是描述需要执行的操作,就像发布一个招标公告。系统会通过Intent Filter匹配符合条件的组件。典型结构:
xml复制<!-- 在接收方AndroidManifest.xml中声明 -->
<activity android:name=".ShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
匹配过程涉及三个核心要素:
- Action:必须至少匹配一个声明的action
- Category:Intent中的所有category都必须能在filter中找到
- Data:包括URI结构和MIME类型的匹配
踩坑提示:如果隐式Intent启动Activity时出现
ActivityNotFoundException,90%的情况是忘记在Intent中添加CATEGORY_DEFAULT。这是系统内部匹配时的默认category,即使你在filter中声明了也必须显式添加。
3. Intent Filter的深度匹配机制
3.1 优先级判定规则
当多个组件声明处理同一Intent时,系统按以下顺序确定优先级:
- 通过
<priority>标签设置数值(0-1000,值越大优先级越高) - 匹配更具体data类型的组件(比如image/png比image/*优先级高)
- 安装时间更近的应用(用户最近安装的更可能符合当前需求)
测试用例表明,以下Intent Filter配置会让你的Activity成为默认图片查看器:
xml复制<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:mimeType="image/*" />
<data android:scheme="content" android:mimeType="image/*" />
<data android:scheme="file" android:mimeType="image/*" />
</intent-filter>
3.2 动态注册的Filter陷阱
BroadcastReceiver可以代码动态注册IntentFilter,但有两个致命限制:
- 动态注册的优先级永远低于静态注册(Android 8.0+)
- 应用进程被杀后动态注册会失效
实测发现,在Android 12上,即使给动态注册的Receiver设置1000的优先级:
java复制IntentFilter filter = new IntentFilter("com.example.MY_ACTION");
filter.setPriority(1000);
registerReceiver(myReceiver, filter);
当系统内存紧张时,该Receiver仍可能收不到静态注册的低优先级广播。解决方案是对于关键广播,必须采用静态注册+权限控制。
4. Intent的高级应用技巧
4.1 安全的PendingIntent
PendingIntent本质上是对Intent的令牌化封装,常见于通知栏点击和AlarmManager。最危险的误用是:
java复制// 错误示范:这样创建会导致任何拿到PendingIntent的人都能以你的身份执行操作
PendingIntent.getActivity(context, requestCode, intent, FLAG_UPDATE_CURRENT);
正确做法应该添加权限限制:
java复制Intent intent = new Intent(this, SecretActivity.class);
// 只有相同UID的应用才能触发
PendingIntent pi = PendingIntent.getActivity(
this,
requestCode,
intent,
FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE
);
4.2 大数据传输的优化
当Intent携带超过1MB数据时,可能出现TransactionTooLargeException。实测传输方案对比:
| 方案 | 最大数据量 | 优缺点 |
|---|---|---|
| putExtra()直接传递 | 0.5MB | 简单但容易崩溃 |
| 单例内存共享 | 不受限 | 需要强引用管理 |
| 文件/ContentProvider | 仅受存储限制 | 需要权限处理 |
| Messenger/IBinder | 1MB | 跨进程自动序列化 |
我的经验是:对于图片等大文件,最佳实践是通过FileProvider生成content://URI传递,并在目标端设置StrictMode检查:
java复制// 发送方
File file = new File(getCacheDir(), "temp.jpg");
Uri contentUri = FileProvider.getUriForFile(this, "com.example.fileprovider", file);
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
// 接收方
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectContentUriWithoutPermission()
.penaltyLog()
.build());
4.3 隐式Intent的验证策略
启动隐式Intent前必须验证是否有组件能处理,否则会导致应用崩溃。推荐双重检查:
java复制// 方法1:PackageManager直接查询
List<ResolveInfo> activities = getPackageManager()
.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (activities.isEmpty()) {
// 显示备用方案
}
// 方法2:安全启动(Android 11+)
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Toast.makeText(this, "没有找到处理程序", Toast.LENGTH_SHORT).show();
}
5. 版本适配关键变更
5.1 Android 12的受限广播
从Android 12开始,对ACTION_BOOT_COMPLETED等广播增加了限制:
- 必须静态注册
- 必须声明
android:exported="true|false"显式声明 - 应用必须在前台运行过至少一次
这导致很多后台服务无法正常接收开机广播。替代方案是改用WorkManager的周期性任务:
kotlin复制val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val request = PeriodicWorkRequestBuilder<MyWorker>(1, TimeUnit.HOURS)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"my_work",
ExistingPeriodicWorkPolicy.KEEP,
request
)
5.2 Android 13的运行时权限
针对通知栏PendingIntent,Android 13新增要求:
xml复制<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
否则创建的PendingIntent会静默失败。调试时可以在开发者选项中开启"PendingIntent结果提示"来发现问题。
