Android 12+下能否通过MediaStore访问USB OTG设备中的图片?
问题解答
核心结论
不能直接通过MediaStore访问USB OTG设备(SD卡读卡器)中的图片。
MediaStore.Images.Media.EXTERNAL_CONTENT_URI仅负责索引设备内部存储、内置SD卡的媒体文件,USB OTG这类通过Storage Access Framework(SAF)挂载的可移除外部存储,不在MediaStore的自动索引范围内,Android 12+也未改变这一机制。
SAF性能优化方案
既然必须使用SAF,针对你当前遍历文件对比大小和修改时间的场景,可以通过以下方式大幅提升性能:
优化思路
避免使用DocumentFile.listFiles()逐个遍历文件(此方法会发起大量独立的ContentResolver请求,性能极低),改用批量查询的方式直接获取目标目录下所有文件的大小和修改时间,再做匹配。
优化后的代码示例
// 替换原onActivityResult中的逻辑 override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) { scope.launch { val treeUri = data?.data ?: return@launch // 构建用于查询子文件的URI val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree( treeUri, DocumentsContract.getTreeDocumentId(treeUri) ) // 定义要查询的字段:文件ID、大小、修改时间、文件名 val projection = arrayOf( DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_SIZE, DocumentsContract.Document.COLUMN_LAST_MODIFIED, DocumentsContract.Document.COLUMN_DISPLAY_NAME ) activity.contentResolver.query(childrenUri, projection, null, null, null)?.use { cursor -> // 遍历游标,批量获取文件信息 while (cursor.moveToNext()) { val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_SIZE)) val lastModified = cursor.getLong(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_LAST_MODIFIED)) filesToMatch?.forEach { fileToMatch -> if (fileSize == fileToMatch.length() && lastModified == fileToMatch.lastModified()) { // 获取匹配文件的URI val docId = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DOCUMENT_ID)) val fileUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, docId) // 执行你的操作 } } } } } }
额外优化建议
- 如果需要递归查询子目录,可以通过递归调用上述查询逻辑,针对每个子目录的文档ID构建新的查询URI。
- Android 12+推荐使用
registerForActivityResult替代startActivityForResult,避免过时API:
private val openDocumentTreeLauncher = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { treeUri -> treeUri?.let { // 执行上述批量查询逻辑 } } // 启动选择器的方法 fun startIntent() { val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager storageManager.storageVolumes.forEach { volume -> if (volume.isRemovable) { val intent = volume.createOpenDocumentTreeIntent() openDocumentTreeLauncher.launch(intent) } } }
内容的提问来源于stack exchange,提问作者Brien Crean
相关产品推荐
相关产品推荐

