Android升级API后如何从文件选择器获取选中文件夹完整路径
问题原因
从Android 10(API 29)引入分区存储机制,Android 11(API 30)开始强制执行后,系统文件选择器返回的均为content://格式的Uri,不再直接暴露文件的真实磁盘路径。你之前通过:分割Uri路径获取真实路径的方法属于非官方的黑盒用法,本身就不具备版本兼容性,高版本系统自然失效。
你当前拿到的document/xxx是Content Provider的内部路径参数,和文件的实际存储路径没有任何关系,直接把这个路径传给File类当然无法得到正确结果。
解决方案
你当前的场景只需要给用户展示友好的路径提示,不需要真正拿到磁盘路径读写,所以直接使用系统提供的DocumentsContract API查询Uri对应的显示信息即可,完全符合官方规范,兼容性更好。
第一步:新增路径查询工具方法
fun getFriendlyDisplayPath(context: Context, uri: Uri): String? { // 处理content://类型的Uri if (ContentResolver.SCHEME_CONTENT == uri.scheme) { val projection = arrayOf( DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_RELATIVE_PATH ) context.contentResolver.query(uri, projection, null, null, null)?.use { cursor -> if (cursor.moveToFirst()) { val fileName = cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_DISPLAY_NAME)) // API 26及以上支持获取相对路径,格式类似Downloads/myPicture/ val relativePath = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { cursor.getString(cursor.getColumnIndexOrThrow(DocumentsContract.Document.COLUMN_RELATIVE_PATH)) ?: "" } else { "" } return relativePath + fileName } } } // 兼容旧版file://类型的Uri else if (ContentResolver.SCHEME_FILE == uri.scheme) { return uri.path } return null }
第二步:替换原有路径获取逻辑
把你原来的路径处理代码:
val path = uri.path if(path == null) { StaticStore.showShortMessage(this, getString(R.string.file_extract_semi).replace("_",file.name)) return@registerForActivityResult } val f = File(path) val p = f.absolutePath.split(":")[1]
替换为:
val p = getFriendlyDisplayPath(this, uri) ?: run { StaticStore.showShortMessage(this, getString(R.string.file_extract_semi).replace("_",file.name)) return@registerForActivityResult }
补充说明
- 你当前的文件读写逻辑(通过
ContentResolver.openFileDescriptor获取文件描述符读写)完全符合官方分区存储规范,不需要修改。 - 如果确实有业务场景必须获取真实磁盘路径,需要申请
MANAGE_EXTERNAL_STORAGE权限,但该权限仅允许文件管理、备份类等有强需求的应用上架谷歌商店,非必要不建议申请。
内容的提问来源于stack exchange,提问作者Mandarin Smell
相关产品推荐
相关产品推荐

