Android 11下设置音频文件为铃声原有代码失效问题求助
问题成因
- Android 11开始分区存储强制生效,
requestLegacyExternalStorage仅在targetSdkVersion ≤29时有效,当targetSdkVersion ≥30时该配置完全失效,应用无法通过传统File类直接操作公共目录下的媒体文件元数据。 MediaStore.MediaColumns.DATA字段在Android 10之后就已被官方废弃,系统不再信任第三方应用传入的本地路径值,导致你插入媒体库的音频元数据无法和实际文件关联,系统只能用媒体库条目ID作为名称展示,就是你看到的数字串。- 你代码中给
MediaStore.Audio.Media.ARTIST传入的是R.string.app_name,这是int类型的资源ID而非实际的字符串值,存入媒体库的就是数字资源ID,也会导致名称显示异常。
解决方案
1. 调整文件写入+媒体库插入逻辑
不要先写本地文件再插入媒体库,改为先向MediaStore申请插入条目拿到Uri,再通过该Uri打开输出流写入音频数据,完全适配分区存储规则,示例代码如下:
// 适配Android 11+的音频保存+插入媒体库逻辑 ContentResolver mCr = v.getContext().getContentResolver(); ContentValues values = new ContentValues(); // 不需要填写已废弃的DATA字段 values.put(MediaStore.MediaColumns.TITLE, name + ".mp3"); values.put(MediaStore.MediaColumns.DISPLAY_NAME, name + ".mp3"); values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mpeg"); values.put(MediaStore.Audio.Media.ARTIST, v.getContext().getString(R.string.app_name)); // 资源ID转字符串 values.put(MediaStore.Audio.Media.IS_RINGTONE, true); values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false); values.put(MediaStore.Audio.Media.IS_ALARM, false); values.put(MediaStore.Audio.Media.IS_MUSIC, false); values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_RINGTONES); // 指定存储到铃声目录 // 先插入媒体库拿到专属Uri Uri audioUri = mCr.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values); if (audioUri == null) { Toast.makeText(v.getContext(), "创建音频条目失败", Toast.LENGTH_SHORT).show(); return; } // 通过Uri打开输出流写入音频数据 try { AssetFileDescriptor soundFile = mCr.openAssetFileDescriptor(mUri, "r"); if (soundFile == null) return; FileInputStream fis = soundFile.createInputStream(); OutputStream fos = mCr.openOutputStream(audioUri); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.close(); fis.close(); soundFile.close(); } catch (IOException e) { // 写入失败删除无效的媒体库条目 mCr.delete(audioUri, null, null); e.printStackTrace(); }
2. 调整铃声设置逻辑
拿到上面生成的audioUri直接设置为铃声即可,原有逻辑基本不需要修改,可增加媒体扫描触发操作确保系统更新索引:
try { if (checkSystemWritePermission()) { RingtoneManager.setActualDefaultRingtoneUri(popUp.this, RingtoneManager.TYPE_RINGTONE, audioUri); // 触发系统媒体扫描更新索引,避免名称显示延迟 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, audioUri)); Toast.makeText(popUp.this, "设置铃声成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(popUp.this, "请开启修改系统设置权限", Toast.LENGTH_LONG).show(); } } catch (Exception e) { Log.i("rington",e.toString()); Toast.makeText(popUp.this, "设置铃声失败", Toast.LENGTH_SHORT).show(); }
3. 权限配置补充
如果你的应用targetSdkVersion ≥33,还需要在AndroidManifest.xml中补充音频读取权限:
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" />
内容的提问来源于stack exchange,提问作者Rusei Marius
相关产品推荐
相关产品推荐

