Android 30及以上版本如何通过MediaQuery搜索PDF文件?
Android 10+ 访问沙箱外PDF文件的解决方案
1. 权限修正
你当前使用的READ_MEDIA_IMAGES仅授权访问图片、视频类媒体资源,无法读取PDF这类文档文件,需根据系统版本适配权限:
- Android 13+(API33及以上):在Manifest中添加
READ_MEDIA_DOCUMENTS权限,动态申请该权限 - Android 10-12(API29-32):依赖
READ_EXTERNAL_STORAGE权限,同样需要动态申请
Manifest权限声明示例:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_MEDIA_DOCUMENTS" android:minSdkVersion="33" />
2. 修复MediaStore查询逻辑
问题点说明
- API29+限制直接通过
DATA列获取文件路径,强制使用Content Uri访问文件,避免路径失效或权限问题 - 需确保查询逻辑匹配标准PDF的MIME类型(
application/pdf)
修改后的代码示例
private fun getPdfList(): ArrayList<Uri>? { val pdfList = ArrayList<Uri>() val projection = arrayOf( MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DISPLAY_NAME, MediaStore.Files.FileColumns.MIME_TYPE ) val sortOrder = "${MediaStore.Files.FileColumns.DATE_ADDED} DESC" val selection = "${MediaStore.Files.FileColumns.MIME_TYPE} = ?" val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("pdf") ?: "application/pdf" val selectionArgs = arrayOf(mimeType) val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL) } else { MediaStore.Files.getContentUri("external") } // 按系统版本检查对应权限 val hasPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_MEDIA_DOCUMENTS) == PackageManager.PERMISSION_GRANTED } else { ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED } if (hasPermission) { contentResolver.query(collection, projection, selection, selectionArgs, sortOrder)?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID) val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn) val fileName = cursor.getString(nameColumn) val contentUri = ContentUris.withAppendedId(collection, id) pdfList.add(contentUri) Log.e("TAG", "PDF File Name: $fileName, Uri: $contentUri") } } } else { // 触发权限申请 requestRequiredPermissions() } return pdfList } // 权限申请方法示例 private fun requestRequiredPermissions() { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf(Manifest.permission.READ_MEDIA_DOCUMENTS) } else { arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) } ActivityCompat.requestPermissions(this, permissions, 1001) }
3. 替代方案:存储访问框架(SAF)
SAF并非仅Android 10可用,API19及以上即可使用,是Scoped Storage推荐的文件访问方式:
- 调用SAF让用户选择单个PDF文件或整个文件夹,无需特殊权限即可访问选中内容
- 可通过
ACTION_OPEN_DOCUMENT或ACTION_OPEN_DOCUMENT_TREE发起选择,获取的Uri可通过takePersistableUriPermission长期保存
示例代码(选择单个PDF):
private fun pickPdf() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type = "application/pdf" } startActivityForResult(intent, 1002) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 1002 && resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> // 获取持久化权限 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) // 使用uri访问PDF文件 Log.e("TAG", "Selected PDF Uri: $uri") } } }
额外注意事项
- 若下载的PDF未被MediaStore索引,可通过
MediaScannerConnection触发媒体扫描,确保文件被收录 - API29+禁止直接操作沙箱外的文件路径,所有文件操作需通过Content Uri或SAF进行
内容的提问来源于stack exchange,提问作者Sunil Chaudhary
相关产品推荐
相关产品推荐

