Android通过URI访问文件触发SecurityException重启后无法播放音频
解决重启后MediaPlayer通过URI播放音频的SecurityException问题
先看错误日志里的核心提示:
java.lang.SecurityException: Permission Denial: opening provider com.android.externalstorage.ExternalStorageProvider ... requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
这个问题的根源很明确:你用的content://类型URI对应的是临时文件访问权限,设备重启后系统会自动回收这个临时权限,导致后续无法再通过该URI访问文件。
问题出在哪:
- 你通过Intent传递
File对象,但它对应的content://URI没有被持久化权限 - 重启后临时权限失效,系统拒绝你的应用访问该URI指向的文件
具体修复步骤:
1. 持久化URI访问权限
当你通过ACTION_OPEN_DOCUMENT或类似存储访问框架API让用户选择文件后,必须立即获取持久化权限,这样重启后权限依然有效。在获取URI的回调中添加这段代码:
// 假设uri是用户选择文件后返回的Content URI int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); getContentResolver().takePersistableUriPermission(uri, takeFlags);
这一步是核心,它告诉系统你的应用需要长期保留该URI的访问权限。
2. 传递URI字符串而非File对象
不要通过Intent传递File对象,直接传递URI的字符串形式更可靠:
// 启动Service时传递URI字符串 Intent sceneService = new Intent(context, SceneManager.class); sceneService.putExtra("audio_uri", mFile.getUri().toString()); context.startForegroundService(sceneService);
在Service中接收并解析URI:
@Override public int onStartCommand(Intent intent, int flags, int startId) { String uriStr = intent.getStringExtra("audio_uri"); Uri audioUri = Uri.parse(uriStr); GameAudioPlayer player = GameAudioPlayer.getInstance(); player.startScene(this, audioUri); // ... 其他业务逻辑 }
3. 调整MediaPlayer的初始化方式
建议显式初始化MediaPlayer并传入ContentResolver和URI,替代MediaPlayer.create的方式(避免内部权限处理的潜在问题):
// 在GameAudioPlayer中 MediaPlayer player = new MediaPlayer(); try { player.setDataSource(context, audioUri); player.prepare(); // 如果需要异步加载可以用prepareAsync() player.start(); } catch (IOException e) { e.printStackTrace(); // 处理播放失败的异常情况 }
额外注意点:
- 如果音频文件是应用自己生成并保存在外部存储私有目录的,建议用
FileProvider生成URI,这类URI不需要额外申请持久化权限 - Android 10及以下需要在Manifest中声明
READ_EXTERNAL_STORAGE权限,Android 11+则不需要,因为存储访问框架的URI权限已经足够
内容的提问来源于stack exchange,提问作者WinterMute
相关产品推荐
相关产品推荐

