Sot*_*nas 16 java android file
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() + ".");
}
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,这可以正常工作。但是,几次会引发异常,这意味着该目录不存在并且无法创建。每运行100次,它就可以在95-96次上正常运行,而失败4-5次。
String myPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getPath();那么,您对为什么发生此问题有任何想法吗?
有没有人经历过类似的事情?
有时“文档”文件夹的路径是“ / storage / emulated / 0 / Documents”,有时又变成同一物理设备上的其他文件吗?
我是一位经验丰富的Android开发人员,但在Android体系结构和Android文件系统方面还是一个新手。可能是在启动时(设备开机或重新启动后)文件系统尚未在我的代码检查目录是否存在时“挂载”“磁盘”吗?在这里,我尽可能宽松地使用术语“安装”和“磁盘”。另外,我的应用程序实际上是启动器/家长控制应用程序,因此这是设备启动时首先触发的内容。我几乎确信这根本没有道理,但在这一点上,我试图查看更广阔的前景,并探索超越典型Android开发的解决方案。
在这个问题开始困扰我的时候,我将非常感谢您的帮助。
期待任何有用的回应。
提前致谢。
编辑(27/08/2019):
我遇到了这个Java Bug报告,尽管它已经过时了。据此,在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()其他方法代替,所以我想指出,我对此感兴趣的是低估了该方法失败的原因,而不是一种可以替代的解决方法。
即使我想 File.exists()用其他东西代替,我也无法做到,因为这段代码驻留在RealmConfiguration.java文件(只读)中,该文件是我在应用程序中使用的Realm库的一部分。
为了使事情更加清楚,我将提供两段代码。因此,我在活动中使用的代码和调用的方法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();
}
Run Code Online (Sandbox Code Playgroud)
此时,myFile存在,并且RealmConfiguration.java调用了get中的代码。
RealmConfiguration.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;
}
Run Code Online (Sandbox Code Playgroud)
因此,myFile存在于我的活动中,调用了Realm代码,突然myFile不再存在。.我再次指出这是不一致的。我注意到崩溃的发生率为4-5%,这意味着myFile在大多数情况下都存在于活动中,并且在领域代码进行检查时都存在。
我希望这会有所帮助。
再次感谢!
首先,如果您使用的是Android,则Java Bugs数据库中的错误报告不相关。Android不使用Sun / Oracle代码库。Android最初是对Java类库的无尘室重新实现。
因此,如果File.exists()Android中存在错误,则这些错误将存在于Android代码库中,而所有报告都将存在于Android问题跟踪器中。
但是当你这样说:
据此,在对NFS挂载的卷进行操作时,java.io.File.exists最终将执行stat(2)。如果统计信息失败(可能由于多种原因而导致),那么
File.exists(错误地)认为正在统计的文件不存在。
File.exists 无法报告任何错误。签名不允许它引发IOException,并且引发未经检查的异常将是一个重大更改。它只能说是true或false。false,则应改用较新的Files.exists(Path, LinkOptions...)方法。这可能是我麻烦的根源吗?
是的,它可以,而不仅限于NFS!见下文。(使用Files.existNFS stat失败很可能是EIO,并且将引发IOException而不是返回false。)
Android代码库(版本android-4.2.2_r1)中的File.java代码为:
public boolean exists() {
return doAccess(F_OK);
}
private boolean doAccess(int mode) {
try {
return Libcore.os.access(path, mode);
} catch (ErrnoException errnoException) {
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,它是如何将其ErrnoException变成的false。
进一步的挖掘表明os.access调用正在执行本机调用,该access调用将进行syscall,并ErrnoException在syscall失败时引发。
因此,现在我们需要查看访问系统调用的已记录行为。内容如下man 2 access:
在以下情况下,access()将失败:
EACCES请求的访问将被拒绝到文件,或搜索?路径名的路径前缀中的目录之一被拒绝执行任务。(另请参见path_resolution(7)。)
ELOOP在解析路径名时遇到太多符号链接。
ENAMETOOLONG路径名太长。
ENOENT路径名的组件不存在或是悬挂的符号链接。
ENOTDIR在路径名中用作目录的组件实际上不是目录。
要求对只读文件系统上的文件具有EROFS写许可权。
在以下情况下,access()可能会失败:
EFAULT路径名指向您可访问的地址空间之外。
EINVAL模式指定不正确。
EIO发生I / O错误。
ENOMEM可用的内核内存不足。
ETXTBSY已请求对正在执行的可执行文件进行写访问。
我已经剔除了我认为在技术上不可能或难以置信的错误,但是仍然有很少的问题需要考虑。
另一种可能性是(例如,应用程序的其他部分)正在删除或重命名文件或(假想的)符号链接,或更改文件权限...。
但是我不认为这File.exist()坏了1,或者说主机操作系统坏了。从理论上讲这是可能的,但是您需要一些明确的证据来支持该理论。
1-就其在行为上与该方法的已知行为没有不同的意义而言,它没有被破坏。您可能会争论不休,直到人们意识到行为是否“正确”为止,但是从Java 1.0开始就一直如此,并且不能在OpenJDK或Android中更改它,而不会破坏过去20多年来编写的数千个现有应用程序年份。不会发生的
接下来做什么?
好吧,我的建议是用来strace跟踪您的应用程序正在执行的系统调用,并查看您是否可以从中获得一些线索,以了解为什么某些access系统调用会给您带来意想不到的结果。例如,路径是什么,路径是什么errno。请参阅https://source.android.com/devices/tech/debug/strace。