Android 代码风格指南

翻译状态: 完成进度82%

本文的英文原始文档(Android Code Style Guidelines for Contributors)地址 : http://source.android.com/source/code-style.html

国内用户如果无法直接访问,请自行翻墙。第一次翻译Java类的文档, 如果有差错和不妥之处, 请不吝指出.

Android的代码风格规则

The rules below are not guidelines or recommendations, but strict rules. Android代码贡献者,如果他们不遵守这些规则,那么一般他们的代码不会被接受
并非所有现存的代码遵循这些规则,但所有的新代码希望能如此

Java 语言规则

我们遵循标准的Java编码约定。
我们还添加一些规则:
  1. 异常:不要在没有说明的情况下捕捉和忽略它们。
  2. Exceptions : 不要捕捉一般异常, except in library code at the root of the stack.
  3. Finalizers :一般不使用它们。
  4. Imports : 完全符合引入

Java库规则

这里有一些关于使用android Java库与工具的约定。在某些情况下,该约定在一些重要的方式已经发生了变化,老的代码可能在使用过时的模式或库。当使用这些代码,最好继续保持与已存在的风格一致(参见一致性)当创建新的组件从来不使用废弃库。

Java的风格规则

当所有文件都保持一致的风格时,程序就更容易维护。我们遵循标准的Java编码风格,他们由Sun公司为Java编程语言制定的编码约定,除少数例外,和增加一些。这些风格指南是全面而详细的,在Java社区很常用。

此外,我们执行下面的样式规则:
  1. 注释/Javadoc :使用标准样式写它
  2. 简短的方法 :不写超大的方法
  3. 字段(成员变量):要么是在该文件的顶部,或紧接在使用它们的方法前面。
  4. 局部变量 :限制作用域
  5. 引入 :android;第三方按字母顺序排列;java(x)
  6. 缩进排版 :4个空格,没有制表符(tab)
  7. 行长度 :100个字符
  8. 字段命名 : 非公有的,非静态字段以m开头,静态变量以s开头 。
  9. 括号 :开括号不要独占一行
  10. 注解 :使用标准的注解。
  11. Acronyms are words : Treat acronyms as words in names, yielding XmlHttpRequest , getUrl() , etc.
  12. TODO的风格 :“TODO:在这里写描述”
  13. 一致性 :看看你周围的
  14. 日志记录 :小心日志记录,它开销很大。

Javatests样式规则

1. 测试方法的命名 :testMethod_specificCase是正确的

Java语言规则

异常:不要忽视

有时很容易编写完全忽略异常的代码,比如:
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
    }
}
你绝对不能这样做。
虽然你可能认为你的代码永远不会遇到这个错误条件或处理它并不重要,忽略像上面的异常会在你的代码中给别人埋下地雷,迟早有一天会被绊倒。原则上,你必须在你代码中处理每一个异常。特殊的处理要视情况而定。

任何时候有人使用空的catch子句,他们应该有一个令人毛骨悚然的感觉。
有一定时候,它实际上是正确的事情,但至少你要想一想。在Java中你无法逃避的令人毛骨悚然的感觉。
– James Gosling

可接受的另外一个方案(为了性能考虑)是:

# Throw the exception up to the caller of your method.

void setServerPort(String value) throws NumberFormatException {
    serverPort = Integer.parseInt(value);
}

# Throw a new exception that’s appropriate to your level of abstraction.

void setServerPort(String value) throws ConfigurationException {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new ConfigurationException("Port " + value + " is not valid.");
    }

# Handle the error gracefully and substitute an appropriate value in the catch {} block.

/** 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
    }

# Catch the Exception and throw a new RuntimeException. This is dangerous: only do it if you are positive that if this error occurs, the appropriate thing to do is crash.

/** 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);
    }

异常: 不要捕捉一般异常

有时候人们容易为了偷懒,在捕捉异常时会这样做:

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!
}

你不应该这样做。


Finalizers

它是什么: 当一个对象被垃圾回收时,Finalizers能够去执行某段代码。

优点: 可以方便清理,特别是外部资源。

缺点: 不能保证finalizer什么时候会调用,或甚至根本上没被调用。


Imports

在imports使用通配符
它是什么 :当你想使用foo包中的Bar类,这有两种可能的方式导入它:
  1. import foo.*;
  2. import foo.Bar;
1#的优点 :大大减少了import声明的数目。
2#的优点 :很明显的看到哪些类实际在用,对于维护者而言,代码更具有可读性。
决定 :使用样式#2 import 所有的Android代码。
一个明确的例外是用于Java标准库(使用java.util .*,java.io. *,等等)和单元测试代码(junit.framework .*)。

注释/Javadoc

所有的文件应该有一个在顶部版权声明。 然后紧接着是包和引入语气,每一块以空白行分隔。再下来是类或接口的声明。 在Javadoc注释,描述的类或接口的用途。

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */
public class Foo {
    ...
}

简短的方法

这在一定程度上是可行的,方法应该保持小而直观的反应它的功能。然而得承认,长的方法有时是比较适当的,因此没有死限制放在方法的长度。如果一个方法超过40行,想想是否可以在没有危害程序结构上进行拆分。


局部变量

局部变量的作用范围应保持最小( Effective Java Item 29)。这样,增加代码的可读性和可维护性,降低错误的可能性。
每个变量应该在最里面的代码块声明,括入变量的所有使用。 局部变量应该先声明后使用,尽量在声明局部变量的同时初始化。如果你还没有足够的信息来初始化变量,你应该推迟声明,直到你需要声明它的时候。
// Instantiate class cl, which represents some sort of Set
Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

But even this case can be avoided by encapsulating the try-catch block in a method:

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}
...
// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

Loop variables should be declared in the for statement itself unless there is a compelling reason to do otherwise:

循环变量应该for里面声明,除非有令人信服的理由不这样做:
for (int i = 0; i < n; i++) {
    doSomething(i);
}

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

Imports


缩进

我们使用4个空格作为块缩进。我们从不使用制表符(tab)。 如有疑问,请与你周围的代码保持一致。我们使用8个空格作为换行缩进,包括函数调用,赋值。

例如,这是正确的:

Instrument i
        = someLongExpression(that, wouldNotFit, on, one, line);

这是不正确的:

Instrument i
    = someLongExpression(that, wouldNotFit, on, one, line);

域(Field)命名

  • 非公有,非静态字段命名以m开头。
  • 静态域命名以s开头。
  • 其他字段以小写字母开头。
  • public static final 字段(常量) 全部大写,并用下划线连起来。

例如:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

花括号

花括号没有独自一行,它们与它前面的代码占同一行,所以:

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

We require braces around the statements for a conditional. Except, if the entire conditional (the condition and the body) fit on one line, you may (but are not obligated to) put it all on one line. That is, this is legal:

if (condition) {
    body; // 正确
}
if (condition) body; // 正确

但是,这是不合规范的:

if (condition)
    body; // 槽糕

行长度

每一行代码中的文本应在最多100个字符。

There has been lots of discussion about this rule and the decision remains that 100 characters is the maximum.

例外:如果一个注释行包含一个示例命令或文字网址超过100个字符,该行可能会超过100个字符,方便于剪切与粘贴。

例外:import 行可以超过这个限制,因为人们很少见到他们.这也简化了书写工具。

Java 1.5的注解

Eclipse 代码格式化

你可以导入development/ide/eclipse下的文件,使得Eclipse按照Android代码风格规则。选择 “Window › Preferences › Java › Code Style,使用 “Formatter › Import” ,导入android-formatting.xml,”Organize Imports › Import” 导入 android.importorder.

eclipse tab 设置为4个空格:
Preferences -> General -> Editors -> Text Editors:
Insert spaces for tabs

If you enjoyed this post, make sure you subscribe to my RSS feed!

10 Replies to “Android 代码风格指南”

Leave a Reply

Your email address will not be published. Required fields are marked *