3

我正在尝试简单地添加音频文件。我的解决方案大多有效。但是当文件已经存在于MediaStore. 我仔细查看过的一次也存在于MediaStore该位置的设备上没有文件。

val values = ContentValues().apply {
   put(MediaStore.Audio.Media.RELATIVE_PATH, libraryPart.rootFolderRelativePath) // JDrop/1/1
   put(MediaStore.Audio.Media.DISPLAY_NAME, remoteLibraryEntry.getFilename()) //12.mp3
   put(MediaStore.Audio.Media.IS_PENDING, 1)
   if(mimeType != null)
       put(MediaStore.Audio.Media.MIME_TYPE, mimeType) // audio/mpeg3
}

val collection = MediaStore.Audio.Media
       .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

var uri = ctx.contentResolver.insert(collection, values) // returns null for around 300/2000 files consistently

尝试插入该新文件时,Logcat 会输出以下内容。

2020-01-24 22:27:33.724 4015-7707/? E/SQLiteDatabase: Error inserting title_key= bucket_display_name=1 owner_package_name=shio.at.jdrop parent=79657 volume_name=external_primary title_resource_uri=null _display_name=12.mp3 mime_type=audio/mpeg3 _data=/storage/emulated/0/Music/JDrop/1/1/12.mp3 title= group_id=1569 artist_id=322 is_pending=1 date_added=1579901253 album_id=2958 primary_directory=Music secondary_directory=JDrop bucket_id=687581593 media_type=2 relative_path=Music/JDrop/1/1/ from {P:30220;U:10165}
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: files._data (code 2067 SQLITE_CONSTRAINT_UNIQUE)
    at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
    at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:879)
    at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:790)
    at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:88)
    at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1639)
    at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1494)
    at com.android.providers.media.MediaProvider.insertFile(MediaProvider.java:3050)
    at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:3452)
    at com.android.providers.media.MediaProvider.insert(MediaProvider.java:3240)
    at android.content.ContentProvider$Transport.insert(ContentProvider.java:325)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:164)
    at android.os.Binder.execTransactInternal(Binder.java:1032)
    at android.os.Binder.execTransact(Binder.java:1005)

files._data意味着该文件已经存在于MediaStore. 没有文件JDrop/1/1/12.mp3,它只是在MediaStore,我需要以某种方式摆脱它或为现有MediaStore条目获取一个 OutputStream 并相应地更新它。

我尝试使用以下代码查询IDin没有成功。MediaStore要么找出 要么IDURI很好。此外MediaStore.Audio.Media.DATA,自 SDK 29 起已弃用。所以我想在不使用它的情况下查询它。

if(uri == null) {
    val id: Long = ctx.contentResolver.query(
            collection,
            arrayOf(BaseColumns._ID),
            "${MediaStore.Audio.Media.RELATIVE_PATH}=? AND ${MediaStore.Audio.Media.DISPLAY_NAME}=?",
            arrayOf(libraryPart.rootFolderRelativePath, remoteLibraryEntry.getFilename()),
            null)?.use {
        if (it.moveToNext())
            it.getLong(it.getColumnIndex(BaseColumns._ID))
        else null
    } ?: return false

    uri = Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id!!.toString())
}

编辑 1(查询 _data)

我现在还尝试_data使用我知道无法插入的硬编码路径进行查询,但没有取得多大成功。

val id: Long = ctx.contentResolver.query(
            collection,
            arrayOf(BaseColumns._ID),
            "${MediaStore.Audio.Media.DATA}=?",
            arrayOf("/storage/emulated/0/Music/JDrop/1/1/12.mp3"),
            null)?.use {
        if (it.moveToNext())
            it.getLong(it.getColumnIndex(BaseColumns._ID))
        else null
    } ?: return false

也返回 null 并返回 false。

编辑 2(查询所有内容并查看它返回的内容)

我尝试按照建议对整个集合进行一些测试查询。

class TestQueryObject(val id: Long, val relativePath: String, val displayName: String)
val results = mutableListOf<TestQueryObject>()

ctx.contentResolver.query(
        collection,
        arrayOf(MediaStore.Audio.Media._ID, MediaStore.Audio.Media.RELATIVE_PATH, MediaStore.Audio.Media.DISPLAY_NAME),
        null,
        null,
        null)?.use {
    while (it.moveToNext()) {
        results.add(TestQueryObject(
                id = it.getLong(it.getColumnIndex(MediaStore.Audio.Media._ID)),
                relativePath = it.getString(it.getColumnIndex(MediaStore.Audio.Media.RELATIVE_PATH)),
                displayName = it.getString(it.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
        ))
    }
}

var find12 = results.find { it.displayName == "12.mp3" }

它返回一个包含 2557 个条目的列表。作为第一个示例,名称是"5.mp3"id 是79658相对路径"Music/JDrop/1/1"。前面有一个Music/我不知道的。但 find12 仍然为空。

编辑 3(可能重要或不重要的其他想法)

还可能值得注意的是,它不会发生在我使用 android 10 创建的 Android 模拟器上。但它会发生在我的 OnePlus 6 上,其中包含大约 300 多个文件,并且适用于所有其余部分。我已经在 Android 9 中使用该程序,然后升级到 Android 10。我在某处读到,当您升级到 10 时,来自 Android 9 的文件可能被认为是孤立的,而不是属于您的应用程序(至少当您卸载创建它们的应用程序)。所以我可能无法再访问我正在寻找的媒体商店条目?不过,我也想过。现在无需任何许可,任何应用程序都可以访问阅读媒体。因此,如果这是一个访问问题,则在尝试写入时应该会失败。不是在阅读或找到它时。

此外,正如@CommonsWare 提到的那样,IS_PENDING可能仍设置为1. 我确实将其设置回0我发布的代码下方。但是,每当我在调试时关闭程序时,该代码可能永远不会执行,因为它将在下载和写入文件的部分进行 9/10 次,因为这是迄今为止程序中发生的任何事情的最长时间。

4

1 回答 1

1

我现在发现了问题。感谢@CommonsWare 提及未决位。我可能需要更长的时间才能弄清楚。

有一种方法Uri MediaStore.setIncludePending(Uri uri)可以输入你的 uri,然后你会用女巫返回一个 uri,你可以查询包含的待处理项目。

使用我从这个方法获得的这个新 Urifind12成功返回了我的东西!

val collection = MediaStore.Audio.Media
    .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
        
var uri = ctx.contentResolver.insert(collection, values)

if(uri == null) {

    class TestQueryObject(val id: Long, val relativePath: String, val displayName: String)
    val results = mutableListOf<TestQueryObject>()

    ctx.contentResolver.query(
            MediaStore.setIncludePending(collection),
            arrayOf(MediaStore.Audio.Media._ID, MediaStore.Audio.Media.RELATIVE_PATH, MediaStore.Audio.Media.DISPLAY_NAME),
            null,
            null,
            null)?.use {
        while (it.moveToNext()) {
            results.add(TestQueryObject(
                    id = it.getLong(it.getColumnIndex(MediaStore.Audio.Media._ID)),
                    relativePath = it.getString(it.getColumnIndex(MediaStore.Audio.Media.RELATIVE_PATH)),
                    displayName = it.getString(it.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
            ))
        }
    }

    var find12 = results.find { it.displayName == "12.mp3" }
}

现在我可以开始让它再次真正起作用了。


编辑Android11

它出现在 Android11MediaStore.setIncludePending(collection)现在已弃用,我们应该使用不同的查询方法来接受 bundle

所以它变成了这样

val queryCollection =
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
            MediaStore.setIncludePending(collection)
        else collection

val queryProjection = arrayOf(MediaStore.Audio.Media._ID)

val querySelection = "${MediaStore.Audio.Media.RELATIVE_PATH}=? AND ${MediaStore.Audio.Media.DISPLAY_NAME}=?"
val querySelectionArgs = arrayOf("someRelativePath", "someFilename")

val queryBundle = Bundle().apply {
    putString(ContentResolver.QUERY_ARG_SQL_SELECTION, querySelection)
    putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, querySelectionArgs)
}

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
     queryBundle.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE)

ctx.contentResolver.query(queryCollection, queryProjection, queryBundle, null)?.use {
    ... Do something
}

在 android 11 之前,这些用于在捆绑包中包含待处理项目的变量不可用。

为 android 10 支持启用旧版存储并像 android < 10 一样使用它可能是一个更好的主意,并且只在 android 10 上实现范围存储。该选项在 android 11 上被忽略,但如果应用程序在 android 10 上运行仍然有效.

于 2020-01-24T23:56:53.330 回答