Android中如何获取content://media格式的音频文件Content URI
问题原因
你用ActivityResultContracts.GetContent()获取到的URI来自Downloads Documents Provider,而非系统媒体库的MediaStore Provider。这是因为文件选择器允许用户从任意存储位置(比如下载文件夹、第三方文件管理器)选择文件,不同来源的文件对应不同的Content Provider,URI格式自然不同。你需要的content://media/internal/audio/media/0是MediaStore中音频条目的标准URI,仅对应系统媒体库索引的音频文件。
解决方法
方法1:将现有URI转换为MediaStore音频URI
如果用户选择的文件已经被MediaStore索引,可以通过ContentResolver查询匹配的音频条目,获取对应的MediaStore URI:
fun getMediaStoreAudioUri(contentResolver: ContentResolver, sourceUri: Uri): Uri? { val filePath = getFilePathFromUri(contentResolver, sourceUri) ?: return null val projection = arrayOf(MediaStore.Audio.Media._ID) val selection = "${MediaStore.Audio.Media.DATA} = ?" val selectionArgs = arrayOf(filePath) contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)) return Uri.withAppendedPath(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id.toString()) } } return null } // 辅助方法:从任意URI获取文件路径(适配不同Provider) private fun getFilePathFromUri(contentResolver: ContentResolver, uri: Uri): String? { when (uri.scheme) { "content" -> { val projection = arrayOf(MediaStore.Images.Media.DATA) contentResolver.query(uri, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { return cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)) } } // 处理Downloads Provider的特殊情况 if (uri.authority == "com.android.providers.downloads.documents") { val id = DocumentsContract.getDocumentId(uri) val contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), id.toLong() ) return getFilePathFromUri(contentResolver, contentUri) } } "file" -> { return uri.path } } return null }
在你的onResult中调用这个方法:
onResult = { uri -> uri?.let { val mediaUri = getMediaStoreAudioUri(contentResolver, it) // mediaUri就是你需要的content://media/...格式 } }
注意:这个方法仅对已经被MediaStore索引的文件有效,如果用户选择的是未被媒体库扫描到的文件(比如刚下载还未被索引的音频),会返回null。
方法2:直接从MediaStore选择音频
如果你只需要用户从系统媒体库中选择音频,可以替换GetContent()为ACTION_PICK对应的ActivityResultContract,直接获取MediaStore格式的URI:
val audioPicker = rememberLauncherForActivityResult( contract = ActivityResultContracts.StartActivityForResult() ) { result -> if (result.resultCode == Activity.RESULT_OK) { val mediaUri = result.data?.data // 这里得到的就是content://media/internal/audio/media/0这类格式的URI } } // 调用方式 val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI) audioPicker.launch(pickIntent)
这种方法的局限性是:用户只能从系统媒体库选择音频,无法选择下载文件夹、SD卡根目录等位置的未索引文件。
注意事项
- Android 10及以上版本的Scoped Storage限制,直接获取文件路径可能会失败,建议优先使用ContentResolver操作URI,而非依赖文件路径。
- 如果必须处理未被MediaStore索引的文件,你可能需要复制文件到应用私有目录,再将其插入MediaStore获取对应的URI。
内容的提问来源于stack exchange,提问作者Venkatesh Paithireddy
相关产品推荐
相关产品推荐

