【Android】Intent
目录
一、什么是Intent
二、显式Intent
三、隐式Intent
四、复杂数据传递
五、跨应用权限管理
六、常见问题
一、什么是Intent
1. 跨组件通信桥梁
- 实现组件间通信(Activity/Service/BroadcastReceiver)
- 封装操作指令与数据传输逻辑
目标组件 | 启动方法 | 数据返回方式 |
---|---|---|
Activity | startActivity() | onActivityResult() |
Service | startService() /bindService() | Binder /Messenger |
BroadcastReceiver | sendBroadcast() | 无(单向通信) |
【Android】四大组件之Activity-CSDN博客文章浏览阅读1.2k次,点赞20次,收藏24次。Activity 是 Android 应用的核心交互组件。_android之activityhttps://blog.csdn.net/qq_15711195/article/details/147526174【Android】四大组件之Service-CSDN博客文章浏览阅读1k次,点赞15次,收藏11次。Service是Android应用的核心后台组件。_android之service
https://blog.csdn.net/qq_15711195/article/details/147531108【Android】四大组件之BroadcastReceiver-CSDN博客文章浏览阅读714次,点赞14次,收藏20次。BroadcastReceiver用于监听系统或应用发出的广播事件,实现跨组件通信。
https://blog.csdn.net/qq_15711195/article/details/1476434862. 两种类型
类型 | 特点 | 适用场景 |
---|---|---|
显式 | 明确指定目标组件类名,如Intent(A.this, B.class) | 应用内部通信 |
隐式 | 通过Action/Category等属性匹配组件 | 跨应用或系统功能调用,如启动系统相机 |
3. 核心属性
属性名 | 功能说明 | 示例用法 |
---|---|---|
Component | 显式指定目标组件类名 | intent.setClass(this, SecondActivity.class) |
Action | 定义执行动作 | intent.setAction(Intent.ACTION_VIEW) |
Category | 补充Action的附加信息 | intent.addCategory(Intent.CATEGORY_DEFAULT) |
Data | 指定操作数据URI | intent.setData(Uri.parse("https://www.example.com")) |
Type | 设置MIME类型(与Data互斥) | intent.setType("image/*") |
Extras | 通过Bundle 携带附加数据 | intent.putExtra("username", "John") |
Flags | 控制启动模式 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) |
二、显式Intent
1. Activity跳转
源activity跳转:
// 基础跳转:无返回函数调用
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
startActivity(intent);// 带返回值跳转:在onActivityResult中接收返回值
startActivityForResult(intent, 100); // 请求码为100
目标Activity返回数据:
// 目标Activity返回数据
Intent resultIntent = new Intent();
resultIntent.putExtra("result", "Success");
setResult(RESULT_OK, resultIntent);
finish();
源activity接收返回结果:
// 接收返回结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {if(requestCode == 100 && resultCode == RESULT_OK) {String result = data.getStringExtra("result"); // 获取返回数据}
}
2. 启动Service
// 启动后台服务
Intent serviceIntent = new Intent(this, DownloadService.class);
serviceIntent.putExtra("url", "https://example.com/file.zip");
startService(serviceIntent); // 执行一次性任务
- 避免在
onPause()
后执行startActivityForResult()
- 及时解绑Service防止内存泄漏
三、隐式Intent
1. 调用系统功能
// 打开网页
Intent webIntent = new Intent(Intent.ACTION_VIEW);
webIntent.setData(Uri.parse("https://www.android.com"));
if (webIntent.resolveActivity(getPackageManager()) != null) {startActivity(webIntent); // 安全检测避免崩溃
}// 分享文本
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "Check this out!");
startActivity(Intent.createChooser(shareIntent, "Share via"));
2. 自定义Action处理
AndroidManifest.xml声明:
<!-- AndroidManifest.xml声明 -->
<activity android:name=".CustomActivity"><intent-filter><action android:name="com.example.ACTION_CUSTOM"/><category android:name="android.intent.category.DEFAULT"/><data android:mimeType="text/*"/></intent-filter>
</activity>
发送自定义Intent:
Intent customIntent = new Intent("com.example.ACTION_CUSTOM");
customIntent.putExtra("custom_data", "Hello Custom");
startActivity(customIntent);
3. 使用resolveActivity()
检测可用组件避免崩溃
if (intent.resolveActivity(packageManager) != null) {startActivity(intent)
}
四、复杂数据传递
1. Bundle封装
发送方:
Bundle bundle = new Bundle();
bundle.putString("name", "Android");
bundle.putInt("version", 13);Intent intent = new Intent(this, TargetActivity.class);
intent.putExtras(bundle);
接收方:
Bundle receivedBundle = getIntent().getExtras();
if(receivedBundle != null) {String name = receivedBundle.getString("name");
}
2. Parcelable对象传输
// 自定义Parcelable类
public class User implements Parcelable {private String name;private int age;// 实现Parcelable接口方法@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeInt(age);}public static final Creator<User> CREATOR = new Creator<User>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}}; // 完整实现需补充
}// 传递对象
User user = new User("John", 25);
intent.putExtra("user", user);
3. 类型冲突处理
同时设置Data和Type时使用setDataAndType():
intent.setDataAndType(Uri.parse("content://contacts/people/1"),"text/vnd.android.cursor.item"
);
五、跨应用权限管理
场景类型 | 权限需求 | 典型示例 |
---|---|---|
启动外部组件 | 声明使用其他应用暴露的权限 | 调用系统相机、地图应用 |
暴露自身组件 | 定义自定义权限保护自身组件 | 允许特定应用访问私有Activity |
1. 声明使用其他应用暴露的权限
示例:调用系统相机拍照并保存到应用私有目录。
在应用AndroidManifest.xml声明所需权限,在FileProvider声明中配置路径:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.app"><!-- 权限声明区域 --><uses-permission android:name="android.permission.CAMERA" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application><!-- FileProvider 组件声明 --><providerandroid:name="androidx.core.content.FileProvider"android:authorities="com.example.fileprovider"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths" /> <!-- 关联外部路径文件 --></provider><!-- 其他组件声明(Activity/Service 等) --></application></manifest>
动态申请危险权限:
private static final int REQUEST_CAMERA_PERMISSION = 100;private void checkCameraPermission() {if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CAMERA},REQUEST_CAMERA_PERMISSION);} else {launchCamera();}
}
处理权限请求结果:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {if (requestCode == REQUEST_CAMERA_PERMISSION) {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {launchCamera();} else {Toast.makeText(this, "需要相机权限", Toast.LENGTH_SHORT).show();}}
}
创建带权限的Intent:
private void launchCamera() {Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (takePictureIntent.resolveActivity(getPackageManager()) != null) {File photoFile = createImageFile(); // 创建临时文件Uri photoURI = FileProvider.getUriForFile(this,"com.example.fileprovider", // 与manifest中配置一致photoFile);// 授予临时访问权限takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);// 系统默认相机应用// 或其他支持 MediaStore.ACTION_IMAGE_CAPTURE Action 的第三方相机应用startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);}
}
2. 定义自定义权限保护自身组件
示例:定义私有Activity只允许持有特定权限的应用访问。
在提供方应用的AndroidManifest.xml定义自定义权限:
<permissionandroid:name="com.example.PRIVATE_ACCESS"android:label="私有访问权限"android:protectionLevel="signature" /> <!-- 只允许相同签名的应用 --><activityandroid:name=".PrivateActivity"android:permission="com.example.PRIVATE_ACCESS"><intent-filter><action android:name="com.example.action.ACCESS_PRIVATE" /><category android:name="android.intent.category.DEFAULT" /></intent-filter>
</activity>
在使用方应用的AndroidManifest.xml声明使用该权限 :
<uses-permission android:name="com.example.PRIVATE_ACCESS" />
使用方发起请求:
public void openPrivateActivity() {try {Intent intent = new Intent("com.example.action.ACCESS_PRIVATE");startActivity(intent);} catch (SecurityException e) {Log.e("Permission", "缺少访问权限: " + e.getMessage());// 处理无权限情况}
}
3. 临时URI权限授予
当通过Intent传递文件URI时,
发送方授予读取权限:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
接收方需在onCreate()中保留权限:
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);getIntent().setFlags(getIntent().getFlags()& ~Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
4. 包可见性适配(Android 11+)
<!-- 在调用方清单文件中声明目标包名 -->
<queries><package android:name="com.target.package" /><!-- 或通过intent-filter声明 --><intent><action android:name="android.intent.action.VIEW" /><data android:scheme="https" /></intent>
</queries>
5. 运行时权限检查
// 检查是否具有自定义权限
private boolean checkCustomPermission() {int result = checkCallingOrSelfPermission("com.example.PRIVATE_ACCESS");return result == PackageManager.PERMISSION_GRANTED;
}// 在组件入口处验证
public class PrivateActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (!checkCustomPermission()) {finish();return;}// 继续执行}
}
- 最小权限原则:只申请必要的权限
- 防御式编程:关键操作前进行权限检查
- 错误处理:捕获
SecurityException
并提供友好提示- 权限文档:在应用描述中说明权限用途
- 兼容性测试:在不同API级别验证权限行为
六、常见问题
问题1:SecurityException: Permission Denial
原因分析:
- 组件未声明所需权限
- 调用方未声明使用权限
- 签名不匹配(针对
protectionLevel="signature"
)
排查步骤:
- 检查双方应用的AndroidManifest权限声明
- 使用
adb shell dumpsys package [package名]
查看权限授予状态 - 验证APK签名是否一致
问题2:FileProvider权限失效
解决方案:
// 授予接收方临时权限列表
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {grantUriPermission(resolveInfo.activityInfo.packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}