看起来这些将成为必须在 Android API 23 或更高版本上运行的核心实用程序/库方法。
关于库方法,我发现最好不要对应用程序将如何使用这些方法做出任何假设。在某些情况下,应用程序可能希望接受检查IOException
(因为文件中的数据必须存在,应用程序才能工作),在其他情况下,应用程序甚至可能不关心数据是否不可用(因为文件中的数据只是缓存,也可以从主源获取)。
当涉及 I/O 操作时,永远无法保证操作会成功(例如,用户将手机掉进厕所)。库应该反映这一点,并让应用程序选择如何处理错误。
为了优化 I/O 性能,始终假设“快乐路径”并捕获错误以找出问题所在。这与正常编程相反,但在处理存储 I/O 时至关重要。例如,在读取文件之前检查文件是否存在可能会使您的应用程序速度减慢一倍 - 所有这些类型的 I/O 操作加起来很快就会减慢您的应用程序速度。假设该文件存在,如果出现错误,则仅检查该文件是否存在。
因此,考虑到这些想法,主要功能可能如下所示:
public static void writeFile(File f, byte[] data) throws FileNotFoundException, IOException {
try (FileOutputStream out = new FileOutputStream(f)) {
out.write(data);
}
}
public static int readFile(File f, byte[] data) throws FileNotFoundException, IOException {
try (FileInputStream in = new FileInputStream(f)) {
return in.read(data);
}
}
实施注意事项:
- 这些方法还可以抛出运行时异常,例如
NullPointerException
s - 这些方法永远不会“没有错误”。
- 我认为上述方法中不需要/不需要缓冲,因为只完成了一次本机调用
(也可以看看here https://www.androidcookbook.info/android-java/bufferedoutputstream-and-bufferedlnputstream.html).
- 应用程序现在还可以选择仅读取文件的开头。
为了使应用程序更容易读取文件,可以添加一个附加方法。但请注意,由库来检测任何错误并将其报告给应用程序,因为应用程序本身无法再检测到这些错误。
public static byte[] readFile(File f) throws FileNotFoundException, IOException {
int fsize = verifyFileSize(f);
byte[] data = new byte[fsize];
int read = readFile(f, data);
verifyAllDataRead(f, data, read);
return data;
}
private static int verifyFileSize(File f) throws IOException {
long fsize = f.length();
if (fsize > Integer.MAX_VALUE) {
throw new IOException("File size (" + fsize + " bytes) for " + f.getName() + " too large.");
}
return (int) fsize;
}
public static void verifyAllDataRead(File f, byte[] data, int read) throws IOException {
if (read != data.length) {
throw new IOException("Expected to read " + data.length
+ " bytes from file " + f.getName() + " but got only " + read + " bytes from file.");
}
}
此实现添加了另一个隐藏的故障点:在创建新数据数组时出现 OutOfMemory。
为了进一步适应应用程序,可以添加其他方法来帮助处理不同的场景。例如,假设应用程序确实不想处理已检查的异常:
public static void writeFileData(File f, byte[] data) {
try {
writeFile(f, data);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
}
public static byte[] readFileData(File f) {
try {
return readFile(f);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
return null;
}
public static int readFileData(File f, byte[] data) {
try {
return readFile(f, data);
} catch (Exception e) {
fileExceptionToRuntime(e);
}
return -1;
}
private static void fileExceptionToRuntime(Exception e) {
if (e instanceof RuntimeException) { // e.g. NullPointerException
throw (RuntimeException)e;
}
RuntimeException re = new RuntimeException(e.toString());
re.setStackTrace(e.getStackTrace());
throw re;
}
方法fileExceptionToRuntime
是一个最小的实现,但它展示了这里的想法。
该库还可以帮助应用程序在发生错误时进行故障排除。例如,一个方法canReadFile(File f)
可以检查文件是否存在、可读且不是太大。应用程序可以在文件读取失败后调用此类函数,并检查无法读取文件的常见原因。写入文件也可以这样做。