android - 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
.
解决方案
在 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
将同时删除索引和内容本身。
推荐阅读
- date - 在飞镖中获取特定日期的开始时间
- javascript - 将自定义 js 添加到特定的 prestashop 页面
- javascript - 如何在 React js 中实现简单的身份验证中间件
- python - 车辆定位的 Keras 模型
- reactjs - setState 与来自 onChange 的数组标签
- python - Azure 服务器未能对请求进行身份验证
- python - 打包一个二级子目录
- firebase - Flutter Firebase Cloud Messaging - 应用程序在后台时的通知
- php - Woocommerce 重复产品然后增加最高 SKU
- angular - 了解 ngOnInit