目录
一、什么是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跳转:
java
// 基础跳转:无返回函数调用
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
startActivity(intent);
// 带返回值跳转:在onActivityResult中接收返回值
startActivityForResult(intent, 100); // 请求码为100
目标Activity返回数据:
java
// 目标Activity返回数据
Intent resultIntent = new Intent();
resultIntent.putExtra("result", "Success");
setResult(RESULT_OK, resultIntent);
finish();
源activity接收返回结果:
java
// 接收返回结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 100 && resultCode == RESULT_OK) {
String result = data.getStringExtra("result"); // 获取返回数据
}
}
2. 启动Service
java
// 启动后台服务
Intent serviceIntent = new Intent(this, DownloadService.class);
serviceIntent.putExtra("url", "https://example.com/file.zip");
startService(serviceIntent); // 执行一次性任务
- 避免在
onPause()
后执行startActivityForResult()
- 及时解绑Service防止内存泄漏
三、隐式Intent
1. 调用系统功能
java
// 打开网页
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声明:
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:
java
Intent customIntent = new Intent("com.example.ACTION_CUSTOM");
customIntent.putExtra("custom_data", "Hello Custom");
startActivity(customIntent);
3. 使用resolveActivity()
检测可用组件避免崩溃
java
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
四、复杂数据传递
1. Bundle封装
发送方:
java
Bundle bundle = new Bundle();
bundle.putString("name", "Android");
bundle.putInt("version", 13);
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtras(bundle);
接收方:
java
Bundle receivedBundle = getIntent().getExtras();
if(receivedBundle != null) {
String name = receivedBundle.getString("name");
}
2. Parcelable对象传输
java
// 自定义Parcelable类
public class User implements Parcelable {
private String name;
private int age;
// 实现Parcelable接口方法
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
dest.writeInt(age);
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
}; // 完整实现需补充
}
// 传递对象
User user = new User("John", 25);
intent.putExtra("user", user);
3. 类型冲突处理
同时设置Data和Type时使用setDataAndType():
java
intent.setDataAndType(
Uri.parse("content://contacts/people/1"),
"text/vnd.android.cursor.item"
);
五、跨应用权限管理
场景类型 | 权限需求 | 典型示例 |
---|---|---|
启动外部组件 | 声明使用其他应用暴露的权限 | 调用系统相机、地图应用 |
暴露自身组件 | 定义自定义权限保护自身组件 | 允许特定应用访问私有Activity |
1. 声明使用其他应用暴露的权限
示例:调用系统相机拍照并保存到应用私有目录。
在应用AndroidManifest.xml声明所需权限,在FileProvider声明中配置路径:
XML
<?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 组件声明 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" /> <!-- 关联外部路径文件 -->
</provider>
<!-- 其他组件声明(Activity/Service 等) -->
</application>
</manifest>
动态申请危险权限:
java
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();
}
}
处理权限请求结果:
java
@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:
java
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定义自定义权限:
XML
<permission
android:name="com.example.PRIVATE_ACCESS"
android:label="私有访问权限"
android:protectionLevel="signature" /> <!-- 只允许相同签名的应用 -->
<activity
android: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声明使用该权限 :
XML
<uses-permission android:name="com.example.PRIVATE_ACCESS" />
使用方发起请求:
java
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时,
发送方授予读取权限:
java
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
接收方需在onCreate()中保留权限:
java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getIntent().setFlags(getIntent().getFlags()
& ~Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
4. 包可见性适配(Android 11+)
XML
<!-- 在调用方清单文件中声明目标包名 -->
<queries>
<package android:name="com.target.package" />
<!-- 或通过intent-filter声明 -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
5. 运行时权限检查
java
// 检查是否具有自定义权限
private boolean checkCustomPermission() {
int result = checkCallingOrSelfPermission("com.example.PRIVATE_ACCESS");
return result == PackageManager.PERMISSION_GRANTED;
}
// 在组件入口处验证
public class PrivateActivity extends Activity {
@Override
protected 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权限失效
解决方案:
java
// 授予接收方临时权限列表
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);
}