You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 02:12:26