You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

面向SDK 30(Android 11)如何打开文件管理器选中的外部文件

问题原因

Android 11(API 30)引入了分区存储强制执行机制,同时收紧了URI的访问权限管控,原调用方式抛出安全异常通常是两类原因:

  • 仅拿到URI的瞬时访问权限,应用退出/重启后再次访问同一URI就会触发权限校验失败
  • 没有正确配置清单文件权限,或存储相关权限申请逻辑不符合分区存储要求
可行解决方案

1. 优先通过系统文件选择器获取永久URI访问权限

不要自行猜测文件路径,统一使用Intent.ACTION_OPEN_DOCUMENT唤起系统文件选择器选文件,选中后主动申请持久化权限,后续就可以长期通过URI访问对应文件:

// 唤起文件选择器选音频的示例代码
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("audio/*");
startActivityForResult(intent, REQUEST_CODE_AUDIO);

在onActivityResult拿到选中的URI后,立即申请持久化权限:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_CODE_AUDIO && resultCode == RESULT_OK && data != null) {
        Uri audioUri = data.getData();
        // 申请持久化访问权限
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(audioUri, takeFlags);
        // 此时再打开输入流就不会抛安全异常
        InputStream inputStream = getContentResolver().openInputStream(audioUri);
    }
}

2. 清单文件权限配置

确保你的AndroidManifest.xml里配置了正确的权限:

<!-- 读取公共媒体文件权限,如果你只需要访问自己应用创建的文件不需要加 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13及以上如果需要访问其他应用创建的音频文件加这个权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<!-- 不要申请MANAGE_EXTERNAL_STORAGE权限,除非你的应用是文件管理器这类需要全局访问文件的工具,否则大概率无法过应用商店审核 -->

3. 临时文件中转方案(适合只需要单次处理音频的场景)

如果你不需要长期访问该文件,拿到URI后可以直接把内容拷贝到你应用的私有目录(context.getFilesDir() 或 context.getCacheDir()),后续直接访问私有目录的文件即可,不需要申请额外权限:

// URI转私有文件示例
public static File uriToFile(Context context, Uri uri, String fileName) throws IOException {
    File targetFile = new File(context.getCacheDir(), fileName);
    try (InputStream is = context.getContentResolver().openInputStream(uri);
         OutputStream os = new FileOutputStream(targetFile)) {
        byte[] buffer = new byte[1024];
        int length;
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);
        }
    }
    return targetFile;
}

内容的提问来源于stack exchange,提问作者user1026605

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 06:45:03