在更新的 Android Q 中,许多事情发生了变化,尤其是范围存储和 file:///
URI 的逐渐弃用。问题是缺少有关如何在 Android Q 设备上正确处理媒体文件的文档。
我有一个媒体文件(音频)管理应用程序,但我找不到可靠的方法来告诉操作系统我对文件进行了更改,以便它可以更新其 MediaStore 记录。
选项 #1:MediaScannerService
MediaScannerConnection.scanFile(context, new String[]{ filePath }, new String[]{"audio/*"}, new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String s, Uri uri) {
}
});
- 使用来自主存储的
file://
URI - 不适用于辅助存储(例如可移动存储)中的
file://
URI - 不适用于任何
content://
URI
选项 #2:广播
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
- 根本不工作
- 即将弃用
选项 #3:手动 MediaStore 插入
AudioFileContentValues
是 MediaStore.Audio.AudioColumns
中的一些列值。
基于 file://
URI 的旧方法:
Uri uri = MediaStore.Audio.Media.getContentUriForPath(file_path);
newUri = context.getContentResolver().insert(uri, AudioFileContentValues);
MediaStore.Audio.Media.getContentUriForPath
已弃用- 还是不行
更新的方法基于我可以从 documentation 中收集的内容:
Uri collection = MediaStore.Audio.Media.getContentUri(correctVolume);
newUri = context.getContentResolver().insert(collection, AudioFileContentValues);
correctVolume
是来自主存储的 external
,而对于辅助存储,它类似于 0000-0000
,具体取决于文件位于。
- 插入返回一个内容 URI,例如
content://media/external/audio/media/125
,但是对于位于主存储中的文件,MediaStore 中没有保留任何记录 - 插入失败,没有返回 URI,MediaStore 中也没有记录
这些或多或少是以前 Android 版本中可用的所有方法,但现在没有一个允许我通知系统我更改了一些音频文件元数据并让 Android 更新 MediaStore 记录。尽管选项 #1 部分有效,但这永远不是一个有值(value)的解决方案,因为它显然不支持内容 URI。
无论文件位于何处,是否有可靠的方法在 Android Q 上触发媒体扫描?根据 Google 的说法,我们甚至不应该关心文件位置,因为我们很快将只使用内容 URI。在我看来,MediaStore 一直有点令人沮丧,但现在情况更糟。
最佳答案
我目前也在为此苦苦挣扎。
我认为一旦您使用了 Android Q,您就不能再做您想做的事情,因为不允许您在 Q 上访问音乐目录。您只能在您创建的目录中创建和访问文件。您没有创建音乐目录。
现在对媒体的每一次改变都必须发生在 MediaStore 上。所以你事先插入你的音乐文件,然后从 MediaStore 中获取一个输出流来写入它。 Q on Media 上的所有更改都应该通过 MediaStore 完成,因此您通知 MediaStore 更改甚至不能再发生,因为您永远不会直接访问文件。
这有一个巨大的缺陷,即 MediaStore 中所有使这成为可能的新事物在旧版本的 Android 中都不存在。所以我目前确实认为您需要将所有内容实现两次,很遗憾。至少如果您想积极影响您的音乐保存位置。
这两个 MediaStore 列在 Q 中是新的,在 Q 之前不存在,您可能需要在 Q 中使用
MediaStore.Audio.Media.RELATIVE_PATH
使用它您可以影响它的保存路径。所以我把“Music/MyAppName/MyLibraryName”放在那里,最终会将“song.mp3”保存到“Music/MyAppName/MyLibraryName/song.mp3”MediaStore.Audio.Media.IS_PENDING
您应该在歌曲仍在编写时将其设置为 1,之后您可以将其更新为 0。
我现在也开始使用 if 检查 Android 版本来实现两次。这很烦人。我不想这样做。但似乎这是唯一的方法。
我只是想在这里放一些代码,说明我如何在 Android.Q 及以下版本上插入音乐。这并不完美。我必须为 Q 指定 MIME 类型,因为 flacs 现在会以某种方式变成 .flac.mp3,因为它似乎不太明白。
所以,无论如何,这是我已经更新的部分,可以与 Q 一起使用,之前,它从我 NAS 上的音乐播放器下载音乐文件。该应用程序是用 kotlin 编写的,不确定这对您来说是否有问题。
override fun execute(library : Library, remoteApi: RemoteApi, ctx: Context) : Boolean {
var success = false
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues().apply {
put(MediaStore.Audio.Media.RELATIVE_PATH, library.rootFolderRelativePath)
put(MediaStore.Audio.Media.DISPLAY_NAME, remoteLibraryEntry.getFilename())
put(MediaStore.Audio.Media.IS_PENDING, 1)
}
val collection = MediaStore.Audio.Media
.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uri = ctx.contentResolver.insert(collection, values)
ctx.contentResolver.openOutputStream(uri!!).use {
success = remoteApi.downloadMusic(remoteLibraryEntry, it!!)
}
if(success) {
values.clear()
val songId = JDrop.mediaHelper.getSongId(uri)
JDrop.db.music.insert(Music(mediaStoreId = songId, remoteId = remoteLibraryEntry.remoteId, libraryId = library.id))
values.put(MediaStore.Audio.Media.IS_PENDING, 0)
ctx.contentResolver.update(uri, values, null, null)
} else {
ctx.contentResolver.delete(uri, null, null)
}
} else {
val file = File("${library.rootFolderPublicDirectory}/${remoteLibraryEntry.getFilename()}")
if(file.exists()) file.delete()
success = remoteApi.downloadMusic(remoteLibraryEntry, file.outputStream())
if (success) {
MediaScannerConnection.scanFile(ctx, arrayOf(file.path), arrayOf("audio/*")) { _, uri ->
val songId = JDrop.mediaHelper.getSongId(uri)
JDrop.db.music.insert(Music(mediaStoreId = songId, remoteId = remoteLibraryEntry.remoteId, libraryId = library.id))
}
}
}
return success
}
这里是 MediaStoreHelper 方法
fun getSongId(uri : Uri) : Long {
val cursor = resolver.query(uri, arrayOf(Media._ID), null, null, null)
return if(cursor != null && cursor.moveToNext()) {
val idIndex = cursor.getColumnIndex(Media._ID)
val id = cursor.getLong(idIndex)
cursor.close()
id
} else {
cursor?.close()
-1
}
}
当您不指定 MIME 类型时,有一件事似乎假定 mp3 是 MIME 类型。所以 .flac 文件将被保存为 name.flac.mp3,因为如果没有 mp3 文件类型并且它认为它是 mp3,它会添加 mp3 文件类型。它不会为 mp3 文件添加另一个 .mp3。我目前在任何地方都没有 MIME 类型...所以我想我现在要继续做这个。
还有一个有用的 google IO 讨论范围/共享存储 https://youtu.be/3EtBw5s9iRY
这可能无法回答您的所有问题。它肯定不适合我。但这是一个有帮助的开端,可以粗略地了解他们甚至从一开始就做了哪些改变。
删除和更新文件在 Q 上有点相同,如果您在媒体存储条目上调用 delete,文件将被删除。之前,Q 你也必须手动删除文件。但如果你在 Q 上这样做,你的应用程序将会崩溃。因此,您必须再次检查您是否使用 Q 或旧版本的 android 并采取适当的措施。
关于android - Android Q 上用于辅助存储的媒体扫描器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56062168/