【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之servicehttps://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;
        }
        // 继续执行
    }
}
  1. 最小权限原则‌:只申请必要的权限
  2. 防御式编程‌:关键操作前进行权限检查
  3. 错误处理 ‌:捕获SecurityException并提供友好提示
  4. 权限文档‌:在应用描述中说明权限用途
  5. 兼容性测试‌:在不同API级别验证权限行为

六、常见问题

问题1SecurityException: Permission Denial

原因分析‌:

  • 组件未声明所需权限
  • 调用方未声明使用权限
  • 签名不匹配(针对protectionLevel="signature"

排查步骤‌:

  1. 检查双方应用的AndroidManifest权限声明
  2. 使用adb shell dumpsys package [package名]查看权限授予状态
  3. 验证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);
}
相关推荐
lucky_tom5 小时前
【android Framework 探究】pixel 5 内核编译
android
NO Exception?5 小时前
完美解决 mobile-ffmpeg Not overwriting - exiting
android·ffmpeg·pcm
0wioiw06 小时前
安卓基础(点击项目)
android·windows
帅得不敢出门6 小时前
Android Framework学习二:Activity创建及View绘制流程
android·java·学习·framework·安卓·activity·window
怀君7 小时前
Flutter——数据库Drift开发详细教程(二)
android·数据库·flutter
wellnw9 小时前
[android]MT6835 Android 关闭selinux方法
android
WenGyyyL11 小时前
《Android 应用开发基础教程》——第十一章:Android 中的图片加载与缓存(Glide 使用详解)
android·缓存·glide
zhangphil12 小时前
Android启动应用时屏蔽RecyclerView滑动,延时后再允许滑动,Kotlin
android·kotlin
帅次12 小时前
Flutter BottomNavigationBar 详解
android·flutter·ios·小程序·iphone·reactnative
是店小二呀14 小时前
【优选算法 | 位运算】位运算基础:深入理解二进制操作
android·c++·算法·性能优化