下述代码风格不是指导或建议,而是严格的规范。向Android贡献的代码如果不遵从这些规范一般不被接受。我们知道并不是所有已存的代码都遵从这些规则,但是我们希望所有的新代码都遵从。
Java语言规则
Android遵从标准的Java代码规范,此外还有下述一些规范。
不要无视异常
一些完全忽略异常的代码书写方式是很诱人的,例如:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
不要这样做,可能你认为你永远不会碰到这个异常或是对这个异常的处理并不重要,像上述代码一样忽略这个异常会对别的接触这个代码的人挖一个大坑。你必须按照一定的原则处理你代码中所有的异常,处理方式视具体的情况而定。
Anytime somebody has an empty catch clause they should have a creepy feeling. There are definitely times when it is actually the correct thing to do, but at least you have to think about it. In Java you can’t escape the creepy feeling. -James Gosling
可以接受的处理方法如下(按优先级排序):
- 将异常抛给方法的调用者。
void setServerPort(String value) throws NumberFormatException {
serverPort = Integer.parseInt(value);
}
- 抛出一个合适的新异常
void setServerPort(String value) throws ConfigurationException {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ConfigurationException("Port " + value + " is not valid.");
}
}
- 优雅的处理这个错误并在catch块里面替换为合适的值
/** Set port. If value is not a valid number, 80 is substituted. */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
serverPort = 80; // default port for server
}
}
- 捕获这个异常并抛出一个新的RuntimeException。这样做是很危险的,所以你一定要确定当这个错误发生的时候程序崩溃是合适。
/** Set port. If value is not a valid number, die. */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new RuntimeException("port " + value " is invalid, ", e);
}
}
Note:原始异常被传给了RuntimeException的构造函数。如果你的代码在Java1.3以下的版本编译,你必须忽略这个异常原因。
- 排在最后一个的是如果你很自信忽略异常是合适的你可以忽略它,但是必须用一个很好的理由注明。
/** If value is not a valid number, original port number is used. */
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) {
// Method is documented to just ignore invalid user input.
// serverPort will just be unchanged.
}
}
不要捕获泛型异常
当像下面代码一样捕获异常对于偷懒是很有诱惑力的:
try {
someComplicatedIOFunction(); // may throw IOException
someComplicatedParsingFunction(); // may throw ParsingException
someComplicatedSecurityFunction(); // may throw SecurityException
// phew, made it all the way
} catch (Exception e) { // I'll just catch all exceptions
handleError(); // with one generic handler!
}
不要这样做,捕获泛型异常或Throwable(最好不是Throwable,因为它包含Error异常)在大多数情况下都是不合适的。它的危险来自于你从未预料到的异常也会在你的应用层错误处理里面捕获。它掩盖了你代码的错误处理能力,意味着如果某人在你调用的代码里面加入了新的异常,编译器不会提醒你要用不同的方式处理这个异常。大多数情况下,你不能用相同的方法处理不同类型的异常。
少量一些情况下是不遵从这些规则的,例如测试代码和想要捕获所有错误的顶层代码(防止他们显示在UI上,或者维持一个批处理的任务运行)。在这种情况下你可能捕获泛型异常(或Throwable)并合适的处理错误。这样做之前你需要仔细斟酌,并解释为什么这么做在这里是安全的。
捕获泛型异常的备用方案:
- 在单独的try块之后用多个catch块分别捕获每个异常。这样可能比较笨拙,但最好捕获所有的异常。注意不要在catch块中有太多重复的代码。
- 使用多个try块重构代码,使代码有更细粒度的错误处理,在每一个try-catch块中分别处理错误。
- 再抛出异常。很多时候你不需要在这个层次捕获异常,只用让方法抛出它就行。
记住:异常是你的朋友!编译器抱怨你没有捕获某个异常的时候,不要皱眉。微笑:编译器只是让你更容易捕获你代码运行时的问题。
不要使用Finalizers
Finalizers是一种可以在一个对象被回收时执行一个代码块的方法。虽然可以用来做一些清理工作(特别是对外部资源),但是并不能保证Finalizers什么时候执行(甚至根本不执行)。
Android不适用Finalizers。在大多数情况下你可以用一个好的异常处理代替finalizer来做一些你想做的工作。如果你确实需要它,必须定义一个close()方法和一个确切的文档来说明这个方法什么时候需要被调用(InputStream为例)。这种情况下,从finalizer打印一条简短的日志消息是不错的,但是只要不希望日志被淹没,这也并不是必须的。
完全限定Imports
在你想调用foo包里面的Bar类时,有两种可能的方式来导入它: import foo.*;
潜在的减少了import语句的数量import foo.Bar;
明确的知道哪个类是被使用的,而且对于维护者来说可阅读性更好。
使用import foo.Bar导入所有的Android代码。一个明确的例外是Java标准库(java.util.* , java.io.* , etc)和单元测试代码(junit.framework.*)。
Java库准则
在使用Android的Java库时有一些习惯。在一些情况下,这些习惯发生了重大改变并且旧代码可能使用了弃用的模式或库。针对这些代码,你可以继续使用已存在的风格。但是在创建新组建的时候,千万不要使用弃用的库。