fil*_*tto 10 android mediastore android-contentresolver android-external-storage android-10.0
随着更新的Android Q的出现,许多事情发生了变化,尤其是在范围存储和file:///URI 逐渐弃用的情况下。问题是缺少有关如何在Android Q设备上正确处理媒体文件的文档。
我有一个媒体文件(音频)管理应用程序,但找不到一种可靠的方法来告诉OS我对文件进行了更改,以便它可以更新其MediaStore记录。
选项1:MediaScannerService
MediaScannerConnection.scanFile(context, new String[]{ filePath }, new String[]{"audio/*"}, new MediaScannerConnection.OnScanCompletedListener() {
@Override
public void onScanCompleted(String s, Uri uri) {
}
});
Run Code Online (Sandbox Code Playgroud)
file://主存储中的URIfile://辅助存储(例如可移动存储)中的URIcontent://URI选项2:广播
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
Run Code Online (Sandbox Code Playgroud)
选项3:手动插入MediaStore
AudioFileContentValues是来自的某些列值MediaStore.Audio.AudioColumns。
基于file://URI的旧方法:
Uri uri = MediaStore.Audio.Media.getContentUriForPath(file_path);
newUri = context.getContentResolver().insert(uri, AudioFileContentValues);
Run Code Online (Sandbox Code Playgroud)
MediaStore.Audio.Media.getContentUriForPath 不推荐使用基于我可以从文档中得出的新方法:
Uri collection = MediaStore.Audio.Media.getContentUri(correctVolume);
newUri = context.getContentResolver().insert(collection, AudioFileContentValues);
Run Code Online (Sandbox Code Playgroud)
哪里correctVolume会external从主存储,同时它会是这样的0000-0000二级存储,根据该文件的位置。
content://media/external/audio/media/125但没有记录保留在MediaStore中,主存储中的文件没有保留这些或多或少是早期Android版本中可用的所有方法,但是现在它们都不允许我通知系统我更改了一些音频文件元数据,并让Android更新MediaStore记录。尽管选项1可以部分起作用,但由于它显然不支持内容URI,因此这永远不是一个有价值的解决方案。
尽管文件位于何处,是否有可靠的方法可以触发Android Q上的媒体扫描?据Google称,我们甚至不必在乎文件的位置,因为我们很快将只使用内容URI。在我看来,MediaStore一直有些令人沮丧,但现在的情况更加糟糕。
我目前也在为此苦苦挣扎。
我认为一旦您使用 Android Q,您就无法再做您想做的事情,因为您不允许访问Q 上的Music 目录。您只能在您创建的目录中创建和访问文件。您没有创建音乐目录。
现在,对媒体的每次更改都必须发生在 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
}
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
当您不指定 MIME 类型时,一件事似乎会假定 mp3 是 MIME 类型。因此 .flac 文件将保存为 name.flac.mp3,因为如果没有 mp3 文件类型,它会添加 mp3 文件类型,并且它认为它是 mp3。它不会为 mp3 文件添加另一个 .mp3。我目前在任何地方都没有 MIME 类型...所以我想我现在要继续执行此操作。
还有一个关于作用域/共享存储的有用的 google IO 讨论https://youtu.be/3EtBw5s9iRY
这可能无法回答您的所有问题。它肯定不适合我。但这是一个有帮助的开始,可以粗略地了解他们一开始做了什么改变。
对于删除和更新文件,它在 Q 上有点相同,如果您对媒体存储条目调用删除,该文件将被删除。在此之前,您还必须手动删除该文件。但如果你在 Q 上这样做,你的应用程序将会崩溃。因此,您必须再次检查您是否使用 Q 或旧版本的 Android 并采取适当的措施。
| 归档时间: |
|
| 查看次数: |
1121 次 |
| 最近记录: |