播放WAV文件时遇权限问题及MediaPlayer报错求助
问题分析与解决方案
核心问题
- 权限适配错误:你的
targetSdk是33(Android 13),该版本开始READ_EXTERNAL_STORAGE权限被废弃,针对音频文件需使用READ_MEDIA_AUDIO权限,旧权限申请逻辑在API33+环境下会直接返回拒绝,不会弹出授权弹窗。 - MediaPlayer数据源潜在问题:
Uri.fromFile()在Android 7.0+可能触发FileUriExposedException,同时权限问题也会导致数据源设置失败。
分步解决
1. 修正Manifest权限声明
替换原有存储权限,适配API33+的媒体权限:
<!-- 适配Android 13+(API33)的音频读取权限 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 兼容Android 12及以下(API32)的存储读取权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
2. 适配动态权限申请逻辑
根据系统版本判断要申请的权限,确保API33+环境下请求正确的媒体权限:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 动态权限申请逻辑 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13+ 申请音频媒体权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_MEDIA_AUDIO}, 1); } } else { // Android 12及以下 申请外部存储读取权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 1); } } }
3. 修复MediaPlayer数据源问题
方案A:直接使用文件路径设置数据源(简单高效)
避免Uri相关的权限和暴露问题,直接传入文件路径:
if (audioFile != null && audioFile.exists()) { mediaPlayer = new MediaPlayer(); try { // 直接用文件路径替代Uri mediaPlayer.setDataSource(audioFile.getAbsolutePath()); mediaPlayer.prepare(); mediaPlayer.start(); } catch (IOException e) { Log.e("MediaPlayer Error", "Error while setting data source: " + e.getMessage()); } catch (IllegalArgumentException e) { Log.e("MediaPlayer Error", "Error while preparing MediaPlayer: " + e.getMessage()); } catch (IllegalStateException e) { Log.e("MediaPlayer Error", "Error while starting MediaPlayer: " + e.getMessage()); } } else { Toast.makeText(MainActivity.this, "No audio file to play", Toast.LENGTH_SHORT).show(); }
方案B:使用FileProvider获取安全Uri(适合共享文件场景)
如果需要用Uri传递文件,配置FileProvider避免文件暴露:
- 在
AndroidManifest.xml的<application>标签内添加Provider声明:
<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>
- 在
res/xml目录下创建file_paths.xml文件:
<?xml version="1.0" encoding="utf-8"?> <paths> <!-- 允许访问外部存储根目录 --> <external-path name="external_files" path="." /> </paths>
- 修改MediaPlayer的Uri获取逻辑:
Uri audioUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".fileprovider", audioFile); mediaPlayer.setDataSource(getApplicationContext(), audioUri);
4. 优化权限回调逻辑
处理用户勾选"不再询问"的场景,引导用户去系统设置开启权限:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1) { boolean isGranted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; if (isGranted) { Toast.makeText(MainActivity.this, "Ja", Toast.LENGTH_SHORT).show(); // 权限获取成功后,可在此触发音频播放 } else { // 判断用户是否勾选了"不再询问" if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) { // 引导用户去应用设置页开启权限 Intent settingIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); settingIntent.setData(uri); startActivity(settingIntent); } else { Toast.makeText(MainActivity.this, "Nein", Toast.LENGTH_SHORT).show(); } } } }
内容的提问来源于stack exchange,提问作者Neo-S
相关产品推荐
相关产品推荐

