Android 11存储权限如何用Java实现?targetSdk30下intent列表为空如何解决
问题原因
- 核心原因是Android 11(API 30)新增的包可见性限制:默认情况下应用无法直接查询设备上其他应用的组件信息,你代码中
addIntentsToList方法内部调用的PackageManager.queryIntentActivities()接口在未声明对应查询规则时会返回空列表,直接导致intentList长度为0。 - 现有代码中使用
Uri.fromFile()传递文件路径的逻辑在Android 7.0及以上版本会触发FileUriExposedException,你当前通过StrictMode临时绕过的方案不合规,Android 11及更高版本运行时也会出现异常。 ACTION_PICK选图逻辑在Android 11分区存储规则下,后续读取图片时会出现权限不足的问题。
解决方案
第一步:添加包可见性声明
在AndroidManifest.xml的<manifest>节点下(和<application>节点平级)添加如下查询规则:
<queries> <!-- 声明可查询响应拍照Intent的应用 --> <intent> <action android:name="android.media.action.IMAGE_CAPTURE" /> </intent> <!-- 声明可查询响应选图Intent的应用 --> <intent> <action android:name="android.intent.action.PICK" /> <data android:mimeType="image/*" /> </intent> </queries>
第二步:替换为FileProvider实现文件分享
移除StrictMode的绕过逻辑,改用官方推荐的FileProvider实现跨应用文件传递:
- 在
res/xml目录下新建file_paths.xml配置文件,内容如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 对应应用私有目录下的Pictures文件夹,可根据自己的临时文件存储路径调整规则 --> <external-files-path name="temp_images" path="Pictures" /> </paths>
- 在
AndroidManifest.xml的<application>节点内添加FileProvider声明:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
- 修改拍照Intent的Uri赋值逻辑:
File tempFile = getTempFile(context); Uri photoUri; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // 7.0及以上用FileProvider生成Uri photoUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", tempFile); } else { // 低版本直接用文件Uri photoUri = Uri.fromFile(tempFile); } takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri); // 给目标相机应用授予Uri读写权限 takePhotoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
第三步:可选优化(适配分区存储)
如果不需要兼容特别老的系统,建议把选图的ACTION_PICK替换为ACTION_GET_CONTENT,不需要申请存储权限就能直接拿到可访问的图片Uri,适配成本更低:
// 替换原来的pickIntent构造逻辑 Intent pickIntent = new Intent(Intent.ACTION_GET_CONTENT); pickIntent.setType("image/*");
内容的提问来源于stack exchange,提问作者Gaurav Parmar
相关产品推荐
相关产品推荐

