File.exists() 为实际存在的文件(目录)返回 false
TLDR:File.exists()
是错误的,我想知道为什么!
我在我的Android应用程序中遇到了一个奇怪的问题(经常发生)。我将尽量简短。
首先,我将向您展示代码,然后提供一些其他信息。这不是完整的代码。只是问题的核心。
示例代码:
String myPath = "/storage/emulated/0/Documents";
File directory= new File(myPath);
if (!directory.exists() && !directory.mkdirs()) {
throw new IllegalArgumentException("Could not create the specified directory: " + directory.getAbsolutePath() + ".");
}
大多数情况下,这工作正常。但是,有几次会引发异常,这意味着该目录不存在,无法创建。在每100次运行中,它在95-96次上工作正常,失败4-5次。
- 我已经在清单中声明了存储/读取外部存储/写入外部存储的权限,并在运行时请求权限。问题不在那里。(如果有的话,我现在有太多的权限:D)。毕竟,如果这是一个权限问题,它每次都会失败,但在我的情况下,它以4%或5%的速率失败。
- 使用上面的代码,我正在尝试创建一个指向“文档”文件夹的文件。在我的应用程序中,我实际上正在使用
在发生错误的特定设备中,此路径恰好是“/存储/模拟/0/文档”,这就是为什么我在我给你的示例代码中硬编码它的原因。String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getPath();
- 如果我在设备上使用文件浏览器应用程序(即“Astro文件管理器”),我可以看到该文件夹确实存在并具有一些内容,并且还确认路径确实是“/storage/emulated/0/Documents”。
- 这在当地从未发生在我身上。只有该应用程序的用户遇到此问题,并且我知道由于Firebase / Crashlytics,该问题仍然存在。用户拥有与我用于开发的平板电脑完全相同的平板电脑,即联想TB-8504X。(我为一家公司工作,我们提供软件和硬件)。
那么,您对为什么会出现此问题有任何想法吗?
有没有人经历过类似的事情?
“文档”文件夹的路径有时是“/存储/模拟/0/文档”,有时成为同一物理设备上的其他内容吗?
我是一个有经验的Android开发人员,但我在Android架构和Android文件系统方面是相当新手。可能是在启动时(当设备打开电源或重新启动后),文件系统尚未“挂载”“磁盘”,而我的代码会检查目录是否存在?在这里,我尽可能松散地使用术语“装载”和“磁盘”。此外,我的应用程序实际上是一个启动器/家长控制应用程序,因此它是设备启动时触发的第一件事。我几乎认为这根本没有意义,但在这一点上,我试图看到更大的图景,并探索超越典型Android开发的解决方案。
我真的非常感谢你的帮助,因为这个问题开始让我紧张。
期待任何有用的回复。
提前致谢。
编辑 (27/08/2019) :
我遇到了这个Java错误报告,尽管它已经过时了。据此,在 NFS 装载的卷上运行时,最终会执行 .如果失败(它可能由于多种原因而失败),则(错误地)假定文件不存在。这可能是我烦恼的根源吗?java.io.File.exists
stat(2)
stat
File.exists
stat'ed
编辑 (28/08/2019) :
今天,我能够为这个问题添加一个赏金,以试图引起更多的关注。我鼓励你仔细阅读这个问题,仔细阅读评论,忽略那些声称这与Realm的客户支持有关的评论。领域代码确实是使用不可靠方法的代码,但我想知道的是为什么该方法不可靠。Realm 是否能够解决这个问题,而是使用其他代码,这超出了问题的范围。我只是想知道是否可以安全地使用,如果没有,为什么?File.exists()
再次,提前感谢大家。对我来说,得到一个答案真的很重要,即使它过于技术性,并且涉及对NFS文件系统,Java,Android,Linux或其他任何东西的更深入理解!
编辑 (30/08/2019) :
因为有些用户建议用其他方法替换,所以我想声明,在这一点上,我感兴趣的是低估了该方法失败的原因,而不是可以使用什么作为解决方法。File.exists()
即使我想用其他东西替换,我也无法做到这一点,因为这段代码驻留在文件(只读)中,这是我在应用程序中使用的 Realm 库的一部分。File.exists()
RealmConfiguration.java
为了使事情更加清晰,我将提供两段代码。我在活动中使用的代码以及因此被调用的方法:RealmConfiguration.java
我在活动中使用的代码:
File myfile = new File("/storage/emulated/0/Documents");
if(myFile.exists()){ //<---- Notice that myFile exists at this point.
Realm.init(this);
config = new RealmConfiguration.Builder()
.name(".TheDatabaseName")
.directory(myFile) //<---- Notice this line of code.
.schemaVersion(7)
.migration(new MyMigration())
.build();
Realm.setDefaultConfiguration(config);
realm = Realm.getDefaultInstance();
}
此时,myFile 存在,并且调用了驻留在 get 中的代码。RealmConfiguration.java
领域配置.java
方法崩溃:
/**
* Specifies the directory where the Realm file will be saved. The default value is {@code context.getFilesDir()}.
* If the directory does not exist, it will be created.
*
* @param directory the directory to save the Realm file in. Directory must be writable.
* @throws IllegalArgumentException if {@code directory} is null, not writable or a file.
*/
public Builder directory(File directory) {
//noinspection ConstantConditions
if (directory == null) {
throw new IllegalArgumentException("Non-null 'dir' required.");
}
if (directory.isFile()) {
throw new IllegalArgumentException("'dir' is a file, not a directory: " + directory.getAbsolutePath() + ".");
}
------> if (!directory.exists() && !directory.mkdirs()) { //<---- Here is the problem
throw new IllegalArgumentException("Could not create the specified directory: " + directory.getAbsolutePath() + ".");
}
if (!directory.canWrite()) {
throw new IllegalArgumentException("Realm directory is not writable: " + directory.getAbsolutePath() + ".");
}
this.directory = directory;
return this;
}
因此,myFile存在于我的活动中,Realm代码被调用,突然myFile不再存在。我想再次指出,这是不一致的。我注意到崩溃的速度为4-5%,这意味着大多数时候myFile都存在于活动中,并且当领域代码进行检查时。
我希望这将是有帮助的。
再次提前致谢!