API 30+(Android 11)下MediaStore.Audio.Media列更新失效问题
Android 11+ 音频标签更新失效问题解决
问题核心原因
Android 11(API 30)及以上版本中,MediaStore元数据与实际音频文件内容强绑定:仅通过ContentResolver.update()修改数据库标签值,系统后续媒体扫描时会重新读取文件原始标签,覆盖你修改的数据库内容。此外你的代码中IS_PENDING机制使用流程不完整,缺少实际修改音频文件标签的关键步骤,这才是导致更新后值恢复的根本原因。
修复步骤
1. 正确使用IS_PENDING锁定媒体项
在修改文件前必须先标记媒体项为pending状态,防止系统扫描干扰修改流程;修改完成后再解除锁定:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentValues pendingValues = new ContentValues(); pendingValues.put(MediaStore.Audio.Media.IS_PENDING, 1); resolver.update(musicUri, pendingValues, null, null); }
注意:此处需单独创建ContentValues设置IS_PENDING,避免和后续标签值混写导致状态覆盖。
2. 实际修改音频文件的标签内容
仅修改MediaStore数据库无效,必须直接修改音频文件本身的标签。可以使用第三方库(如MP3agic)处理MP3文件:
- 先通过
ContentResolver获取文件可写句柄:
ParcelFileDescriptor pfd = resolver.openFileDescriptor(musicUri, "rw"); if (pfd != null) { FileInputStream fis = new FileInputStream(pfd.getFileDescriptor()); FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor()); // 使用MP3agic修改标签 Mp3File mp3file = new Mp3File(fis); ID3v2 id3v2Tag = mp3file.hasId3v2Tag() ? mp3file.getId3v2Tag() : new ID3v24Tag(); // 无标签则创建新的 id3v2Tag.setTitle(title); id3v2Tag.setArtist(artist); id3v2Tag.setAlbum(album); id3v2Tag.setYear(year); mp3file.setId3v2Tag(id3v2Tag); mp3file.save(fos); // 关闭资源 fos.close(); fis.close(); pfd.close(); }
3. 解除IS_PENDING锁定并同步MediaStore
文件修改完成后,解除pending状态,系统会自动读取文件新标签并同步到MediaStore:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentValues pendingValues = new ContentValues(); pendingValues.put(MediaStore.Audio.Media.IS_PENDING, 0); resolver.update(musicUri, pendingValues, null, null); }
此步骤后无需再手动调用resolver.update()修改标签字段,系统会自动完成同步。
4. 权限处理补充
处理RecoverableSecurityException时,需申请对应媒体Uri的修改权限:
try { // 执行修改操作 } catch (RecoverableSecurityException e) { Intent intent = e.getUserAction().getActionIntent(); startActivityForResult(intent, REQUEST_CODE_EDIT_MEDIA); }
在权限授予回调(onActivityResult或registerForActivityResult)中重新执行修改流程。
完整修正后的代码片段
public void changeMp3Tags(Context c, Uri musicUri, String title, String artist, String album, String year) { try { ContentResolver resolver = c.getContentResolver(); // 1. 锁定媒体项(Android Q+) if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentValues pendingValues = new ContentValues(); pendingValues.put(MediaStore.Audio.Media.IS_PENDING, 1); resolver.update(musicUri, pendingValues, null, null); } // 2. 实际修改音频文件标签 ParcelFileDescriptor pfd = resolver.openFileDescriptor(musicUri, "rw"); if (pfd != null) { FileInputStream fis = new FileInputStream(pfd.getFileDescriptor()); FileOutputStream fos = new FileOutputStream(pfd.getFileDescriptor()); Mp3File mp3file = new Mp3File(fis); ID3v2 id3v2Tag = mp3file.hasId3v2Tag() ? mp3file.getId3v2Tag() : new ID3v24Tag(); id3v2Tag.setTitle(title); id3v2Tag.setArtist(artist); id3v2Tag.setAlbum(album); id3v2Tag.setYear(year); mp3file.setId3v2Tag(id3v2Tag); mp3file.save(fos); fos.close(); fis.close(); pfd.close(); } // 3. 解除锁定并同步 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { ContentValues pendingValues = new ContentValues(); pendingValues.put(MediaStore.Audio.Media.IS_PENDING, 0); resolver.update(musicUri, pendingValues, null, null); } // 验证修改结果 String[] songColumns = { MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.YEAR, MediaStore.Audio.Media.IS_PENDING }; Cursor cursor = resolver.query(musicUri, songColumns, null, null, null); if (cursor != null) { cursor.moveToFirst(); Log.d(TAG,"song title: "+cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE))); Log.d(TAG,"song artist: "+cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST))); Log.d(TAG,"song album: "+cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM))); Log.d(TAG,"song year: "+cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.YEAR))); cursor.close(); } }catch (Exception e){ Log.e(TAG, "Exception caught when changing MP3 tags!", e); throw e; } }
注意事项
- 依赖MP3agic库:在build.gradle中添加
implementation 'com.mpatric:mp3agic:0.9.1' - Android 13+ 需额外申请
READ_MEDIA_AUDIO权限 - 需妥善处理文件IO异常,避免资源泄漏
内容的提问来源于stack exchange,提问作者Vince VD
相关产品推荐
相关产品推荐

