Android 11系统使用RingtoneManager设置自定义铃声报错求助
错误触发原因
- Android 11 强制开启分区存储,
MediaStore.MediaColumns.DATA字段已被官方废弃,你代码中通过绝对路径操作MediaStore条目的逻辑不再被系统允许,直接触发文件找不到的ENOENT报错。 - 铃声类型Uri校验规则变更:Android 11要求设置为铃声的音频必须归属系统铃声对应的MediaStore分类,你代码中使用通用的外部音频Uri插入条目,且文件存放在Music目录,不符合铃声Uri校验规则,抛出
Uri不属于铃声、闹钟或通知类型的安全异常。 - 权限缺失:修改系统铃声需要特殊的
WRITE_SETTINGS权限,普通存储权限无法完成该操作。
Android 11 适配方案
步骤1:声明并申请所需权限
首先在AndroidManifest.xml中添加权限声明:
<!-- 安卓10及以下存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <!-- 修改系统设置权限,用于设置铃声 --> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <!-- 安卓13及以上读取音频权限,仅当你需要读取其他应用的音频文件时添加 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
运行时先判断是否有修改系统设置权限,无权限则跳转系统设置页引导用户开启:
private static final int REQUEST_CODE_WRITE_SETTINGS = 1001; // 权限校验逻辑 if (!Settings.System.canWrite(this)) { Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS); intent.setData(Uri.parse("package:" + getPackageName())); startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS); }
步骤2:修改铃声写入逻辑
Android 11及以上版本不要使用DATA字段,也不要用通用的外部音频Uri插入条目,直接通过MediaStore流写入铃声数据:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.MediaColumns.TITLE, "ringtonename"); contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "ringtonename"); contentValues.put(MediaStore.MediaColumns.MIME_TYPE, getMIMEType(ringtone_file.getPath())); contentValues.put(MediaStore.Audio.Media.ARTIST, "ringtonename"); contentValues.put(MediaStore.Audio.Media.IS_RINGTONE, true); contentValues.put(MediaStore.Audio.Media.IS_NOTIFICATION, true); contentValues.put(MediaStore.Audio.Media.IS_ALARM, true); contentValues.put(MediaStore.Audio.Media.IS_MUSIC, false); ContentResolver contentResolver = getContentResolver(); // 直接使用铃声分类的Uri插入 Uri audioUri = MediaStore.Audio.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); Uri insertUri = contentResolver.insert(audioUri, contentValues); if (insertUri != null) { // 通过输出流写入音频数据,替换原有直接写绝对路径的逻辑 OutputStream os = contentResolver.openOutputStream(insertUri); FileInputStream fis = new FileInputStream(ringtone_file); byte[] buffer = new byte[1024]; int len; while ((len = fis.read(buffer)) != -1) { os.write(buffer, 0, len); } fis.close(); os.close(); // 设置默认铃声 RingtoneManager.setActualDefaultRingtoneUri(this, RingtoneManager.TYPE_RINGTONE, insertUri); } } else { // 保留原有Android 10及以下版本的逻辑即可 }
内容的提问来源于stack exchange,提问作者nasser
相关产品推荐
相关产品推荐

