首页 > 解决方案 > Scoped Storage:如何通过 MediaStore 删除多个音频文件?

问题描述

我正在尝试从设备的外部存储(例如/storage/emulated/0/Music文件夹中)中删除音频文件。在分析了MediaStore示例之后,我最终得到了 API 28 及更早版本的以下解决方案:

fun deleteTracks(trackIds: LongArray): Int {
    val whereClause = buildWildcardInClause(trackIds.size) // _id IN (?, ?, ?, ...)
    return resolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, Array(trackIds.size) { trackIds[it].toString() })
}

我注意到上面的代码只从 MediaStore 数据库中删除了曲目,但没有从设备中删除(文件在重新启动后重新添加到 MediaStore)。因此,我修改了该代码以查询该Media.DATA列,然后使用该信息删除相关文件。这按预期工作。

但是现在 Android Q 引入了 Scoped Storage,Media.DATA(和Albums.ALBUM_ART)现在已被弃用,因为应用程序可能无法访问这些文件。ContentResolver.openFileDescriptor只能用来读取文件,不能删除。

那么从 Android Q 开始删除曲目的推荐方法是什么?该示例没有显示如何从 中删除多个文件MediaStore,并且MediaStore.Audio.Media似乎与MediaStore.Images.Media.

标签: androidmediastoreandroid-10.0scoped-storage

解决方案


在 Android 10 中有两种方法可以做到这一点,这取决于android:requestLegacyExternalStorage应用AndroidManifest.xml文件中的值。

选项 1:启用范围存储 ( android:requestLegacyExternalStorage="false")

如果您的应用程序添加了以 (using ) 开头的内容,contentResolver.insert那么上面的方法 (using contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, idsArray)) 应该可以工作。在 Android 10 上,应用程序提交到媒体商店的任何内容都可以由该应用程序自由操作。

对于用户添加的内容,或者属于另一个应用程序的内容,这个过程有点复杂。

要从媒体存储中删除单个项目,请不要将通用媒体存储 URI 传递给delete(即:)MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,而是使用项目的特定 URI。

context.contentResolver.delete(
    audioContentUri,
    "${MediaStore.Audio.Media._ID} = ?",
    arrayOf(audioContentId)

(我很确定没有必要使用“where”子句,但这对我来说很有意义:)

RecoverableSecurityException在 Android 10 上,如果您targetSdkVersion的 >=29 ,这可能会抛出. 在此异常中IntentSender,您可以启动一个异常,Activity以请求用户修改或删除它的权限。(发件人在 中recoverableSecurityException.userAction.actionIntent.intentSender

由于用户必须授予每个项目的权限,因此无法批量删除它们。(或者,可能会请求修改或删除整个专辑的权限,一次一首歌曲,然后使用delete您上面使用的查询。此时您的应用程序将有权删除它们,而 Media Store 会这样做一次。但你必须先要求每首歌。)

选项 2:启用旧版存储 ( android:requestLegacyExternalStorage="true")

在这种情况下,简单地调用contentResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, whereClause, idsArray)不仅会从数据库中删除项目,还会删除文件。

该应用程序需要获得WRITE_EXTERNAL_STORAGE许可才能正常工作,但我测试了您在问题中的代码并验证文件也已被删除。

因此,对于较旧的 API 版本,我认为您需要自己使用contentResolver.delete和取消链接文件(或取消链接文件并提交重新扫描的请求MediaScannerConnection)。

对于 Android 10+,contentResolver.delete将同时删除索引和内容本身。


推荐阅读