如何更新 Android Q 媒体商店中音频文件的元数据?

How to update metadata of audio file in Android Q media store?

更新媒体存储中音频文件的元数据在 Android Q OS 中不起作用,它在所有其他 OS.

中起作用

我正在使用 uri 指定为 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 的内容提供程序。它在 Android Q 设备以下的所有设备中工作正常。下面是我用来更新曲目元数据的代码。

ContentValues cv = new ContentValues();
ContentResolver resolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

cv.put(MediaStore.Audio.Media.TITLE, newTitle);
cv.put(MediaStore.Audio.Media.ALBUM, newAlbumName);
cv.put(MediaStore.Audio.Media.ARTIST, newArtistName);

int rowsUpdated = resolver.update(uri, cv, 
MediaStore.Audio.Media._ID + " = ? ", new String[]{audioId});

对于AndroidQ设备,rowsUpdated始终为0,无一例外。 其他音乐播放器如何更新 Android Q 中的曲目元数据?

使用 Android Q 及以后你必须先获取文件

resolver.openInputStream(uri)?.use { stream -> outputFile.copyInputStreamToFile(stream) }
return outputFile.absolutePath

辅助函数

private fun File.copyInputStreamToFile(inputStream: InputStream?) {
    this.outputStream().use { fileOut ->
        inputStream?.copyTo(fileOut)
    }
}

然后通过第三方修改元数据,我用的是J Audio Tagger

然后覆盖旧文件

// From https://developer.android.com/reference/android/content/ContentProvider
// String: Access mode for the file. May be
// "r" for read-only access,
// "w" for write-only access (erasing whatever data is currently in the file),
// "wa" for write-only access to append to any existing data,
// "rw" for read and write access on any existing data, and
// "rwt" for read and write access that truncates any existing file. This value must never be null.

mContext.application.contentResolver.openOutputStream(uri, "w")?.use { stream ->
            stream.write(file.readBytes())
        }

当文件由您的应用程序创建时,这工作正常

终于,花了一些时间,但我想通了。

首先,您需要获得对文件的访问权限。 Here you can read about that

接下来,我发现要更新标题或艺术家字段(也许其他人,我没有测试它们)你需要将列 MediaStore.Audio.Media.IS_PENDING 的值设置为 1。像这样:

    val id = //Your audio file id

    val values = ContentValues()
    values.put(MediaStore.Audio.Media.IS_PENDING, 1)

    val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
    contentResolver.update(uri, values, null, null)

然后您可以编辑您需要的字段。同样要结束更新过程,请再次将 MediaStore.Audio.Media.IS_PENDING 设置为 0:

    val id = //Your audio file id
    val title = //New title
    val artist = //New artist

    val values = ContentValues()
    values.put(MediaStore.Audio.Media.IS_PENDING, 0)
    values.put(MediaStore.Audio.Media.TITLE, title)
    values.put(MediaStore.Audio.Media.ARTIST, artist)

    val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
    contentResolver.update(uri, values, null, null)

所以在一个函数中,它看起来像这样:

    @RequiresApi(value = android.os.Build.VERSION_CODES.Q)
    fun updateMetadata(contentResolver: ContentResolver, id: Long, title: String, artist: String) {
        val uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
        val values = ContentValues()
        
        values.put(MediaStore.Audio.Media.IS_PENDING, 1)
        contentResolver.update(uri, values, null, null)
        
        values.clear()
        values.put(MediaStore.Audio.Media.IS_PENDING, 0)
        values.put(MediaStore.Audio.Media.TITLE, title)
        values.put(MediaStore.Audio.Media.ARTIST, artist)
        contentResolver.update(uri, values, null, null)
    }

它是用 Kotlin 编写的,但我想您会在 java 中弄清楚如何做到这一点。

更新

通过更新 MediaStore,您不会更新任何 android 版本的真实文件。这意味着,如果一个文件被 MediaScannerConnection 扫描 and/or 更新(例如:重命名),您的更改将会丢失。 This answer 是对的。

我一直在通过 ContentResolver 更新 MediaStore 中的元数据,但这不再适用于 Android Q (API 29)。下面的代码给我一个警告,描述没有更新:

ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DESCRIPTION, "Some text");

res = getContext().getContentResolver().update(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        values,
        MediaStore.Images.Media._ID + "= ?", new String[]{sImageId});

android.process.media W/MediaProvider: Ignoring mutation of description from com.example.android.someapp.app

这个媒体 post 描述了 how Google has changed the API for accessing and updating files,但是只更新 元数据 呢?警告似乎告诉我 Google 不再允许第三方应用程序使用 MediaStore,我也找到了警告的来源: https://android.googlesource.com/platform/packages/providers/MediaProvider/+/master/src/com/android/providers/media/MediaProvider.java#2960