“空指针”引用导致“程序异常退出”两例
发布日期:2021-05-15 16:22:01 浏览次数:17 分类:精选文章

本文共 3495 字,大约阅读时间需要 11 分钟。

最近,开发者在使用Android系统时,偶尔会遇到“程序异常退出”的提示。这类问题往往与资源引用错误或空指针异常有关。通过反复复查代码发现,代码中存在一些潜在的空指针引用问题,我们将从以下两个方面进行详细说明。

首先,分析第一个问题:函数参数传入为空,在函数内部未做参数有效性检查。在实际应用中,这是一个常见的错误。例如,假设有如下代码:

public void run() {
try {
FileEngine.deleteFile(plainFilePath);
// 其他代码
} catch (Exception e) {
e.printStackTrace();
}
}

被调用的FileEngine.deleteFile方法实现如下:

public static boolean deleteFile(String filePath) {
if (filePath == null) {
return false;
}
File file = new File(filePath);
if (!file.exists()) {
return false;
}
if (file.isFile()) {
return file.delete();
} else {
return false;
}
}

这里可以看到,方法已经检查了filePath是否为null,但未能对filePath是否为有效路径或文件是否存在进行充分检查。假设在调用deleteFile时传递了一个无效路径字符串,文件系统可能会抛出异常。然而,如果filePath是一个合法的路径但指向一个不存在的文件或目录,new File(filePath)会正常返回一个非null对象,但file.exists()检查时会返回false,进而注销方法。此外,对于空字符串或无效路径,new File(filePath)的行为可能与预期不同,尤其是在某些特定系统中。

为了避免这种问题,我们可以在调用deleteFile前增加更多的输入检查,或者在方法内部进行更严格的参数验证。例如,可以考虑在调用前使用文件路径检查工具或者文件确认库来确保输入的路径有效。

其次,分析第二个问题:构造函数抛出异常,导致对象未成功创建。假设有如下代码:

LogWriter.getInstance().print("setOnItemLongClickListener===========000====");

对应的LogWriter类实现如下:

public class LogWriter {
private static LogWriter mLogWriter;
private static String mPath;
private static Writer mWriter;
private static SimpleDateFormat df;
private LogWriter() {
mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test";
File file = new File(mPath);
if (!file.exists()) {
file.mkdirs();
}
try {
SimpleDateFormat df0 = new SimpleDateFormat("yy-MM-dd-HH-mm", Locale.getDefault());
File logFile = new File(mPath, "Log-" + df0.format(new Date()) + ".txt");
mWriter = new BufferedWriter(new FileWriter(logFile));
} catch (IOException e) {
e.printStackTrace();
}
df = new SimpleDateFormat("[yy-MM-dd HH:mm:ss]: ", Locale.getDefault());
}
public static LogWriter getInstance() {
if (mLogWriter == null) {
mLogWriter = new LogWriter();
}
return mLogWriter;
}
public void close() throws IOException {
mWriter.close();
}
public void print(String log) {
try {
mWriter.write(df.format(new Date()));
mWriter.write(log);
mWriter.write("\r\n");
mWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}

在上述代码中, singleton模式的实现是通过双重锁机制进行的,但有一个潜在的安全问题。如果在构造函数(LogWriter())内部的初始化过程中发生异常(例如Environment.getExternalStorageDirectory()返回null,或者new SimpleDateFormat抛出异常等),则可能导致mLogWriter没有被初始化为有效对象,而是在后续调用getInstance()时,返回一个未被正确构造的LogWriter实例。

为了防止这种情况,可以在构造函数的调用上增加一个异常捕捉,以确保类的初始化过程的原子性。具体来说,可以将构造函数的关键代码改为:

private LogWriter() {
mPath = ...;
File file = new File(mPath);
if (!file.exists()) {
if (!file.mkdirs()) {
throw new RuntimeException("Failed to create directory: " + mPath);
}
}
try {
mWriter = ...;
if (mWriter == null) {
throw new RuntimeException("Failed to create writer: " + mPath);
}
df = ...;
} catch (IOException e) {
throw new RuntimeException("Initialization of LogWriter failed", e);
}
}

这样可以确保在构造函数内部的异常都会被重抛,避免留下未初始化的单件实例。从而,在调用getInstance()时总能获得一个状态良好的LogWriter实例。

综上所述,在编写代码时,我们应当在函数参数处理上进行有效性检查,在类构造函数中确保初始化的原子性,避免因参数错误或构造异常导致空指针引用的发生。通过这样的代码审查和优化,可以有效减少程序在运行时遇到的异常情况。

上一篇:Android基础#27:Android Context 使用时注意内存泄漏问题
下一篇:Android基础#22:Android handler用法详解2,例2

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年04月28日 03时21分47秒