Android Q及以上版本File.exists()始终返回false该如何解决
问题原因
Android Q(API 29)及以上版本默认启用分区存储特性,应用对外部共享存储的访问被限制,无法直接通过原始文件路径的File API判断文件是否存在,只有应用专属目录(内部存储data/data/包名/、外部存储Android/data/包名/下的路径)可以正常用File API访问。如果你的代码中PATH_SUFFIX指向的是共享存储区域,就会出现文件存在也始终返回false的问题。
如果你的文件确实存放在应用内部专属目录下,还要检查路径拼接逻辑是否存在错误:比如PATH_SUFFIX开头没有添加路径分隔符/,导致拼接出来的完整路径和实际文件路径不一致。
解决方案
- 方案1:文件存放在应用专属目录时,使用官方API获取根路径,避免硬编码拼接错误
private fun isFileExists(): Boolean { var checked = false try { // 内部存储应用专属目录根路径,等价于applicationInfo.dataDir val rootPath = filesDir.absolutePath // 拼接路径时主动添加分隔符,避免拼写错误 val filePath = "$rootPath${File.separator}$PATH_SUFFIX${File.separator}$FILE_NAME" val file = File(filePath) checked = file.exists() } catch (ignored: Exception) { } return checked }
- 方案2:文件存放在公共共享存储区域(比如DCIM、Download、Documents等公共目录)时,改用MediaStore查询判断
private fun isPublicFileExists(fileName: String): Boolean { // 要查询的字段,只需要查ID判断是否存在即可 val projection = arrayOf(MediaStore.Files.FileColumns._ID) // 查询条件:匹配文件名 val selection = "${MediaStore.Files.FileColumns.DISPLAY_NAME} = ?" val selectionArgs = arrayOf(fileName) // 查询所有外部存储的文件 contentResolver.query( MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL), projection, selection, selectionArgs, null )?.use { cursor -> // 游标数量大于0说明存在匹配文件 return cursor.count > 0 } return false }
如果是特定类型的文件,可以替换查询的Uri提升查询效率:比如图片用MediaStore.Images.Media.EXTERNAL_CONTENT_URI,视频用MediaStore.Video.Media.EXTERNAL_CONTENT_URI。
- 方案3:临时兼容方案(不推荐,Android 13及以上版本不再生效)
在AndroidManifest.xml的application节点添加android:requestLegacyExternalStorage="true"属性,请求使用旧版存储权限,即可继续使用原有File API逻辑。
内容的提问来源于stack exchange,提问作者Tarif Chakder
相关产品推荐
相关产品推荐

