搜索
您的当前位置:首页正文

Android编码规范

来源:二三娱乐

Android 编码规范

1. 前言

在项目编码之前统一编码规范是一件很重要的事情,抽空从网上的一些资料整理一份Android的编码规范。

Tips:这份文档存在的意义是为了提高代码的可读性,降低阅读成本。

参考资料

2. 源文件基础

2.1. 文件名

源文件以其最顶层的类名来命名,大小写敏感,文件扩展名为 .java

2.2. 文件编码:UTF-8(重要)

源文件编码格式为UTF-8,这一点非常重要,一定要设置。

2.3. 特殊字符

字符 说明
空白字符 除了行结束符序列,ASCII水平空格符(0x20,即空格)是源文件中唯一允许出现的空白字符,这意味着:
1.所有其他字符串中的空白字符都要进行转义。
2.制表符不用于缩进(可以再IDE中将TAB键设置为若干个空格)。
特殊转义序列 对于具有特殊转义序列的任何字符(\b, \t, \n, \f, \r, ", '及),我们使用它的转义序列,而不是相应的八进制(比如\012)或Unicode(比如\u000a)转义。
非ASCII字符 对于剩余的非ASCII字符,是使用实际的Unicode字符(比如∞),还是使用等价的Unicode转义符(比如\u221e),取决于哪个能让代码更易于阅读和理解。

3. 源文件结构

一个源文件包含(按顺序地):

  1. 许可证或者版权信息(如有需要)
  2. package语句
  3. import语句
  4. 一个顶级类(有且只有一个)的声明

以上每个部分之间用一个空行作为分割。

3.1. 许可证或版权信息

如果一个文件包含许可证或版权信息,那么它应当被放文件最前面。

3.2. package语句

package 语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里)

3.3. import语句

  • 不要使用通配符,即不要出现这样的语句:import java.util*;
  • 不要换行
  • 分组

3.4. 类声明

类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。
最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的。

3.4.1 区域划分(建议)

  1. 常量声明区
  2. UI控件成员变量声明区
  3. 普通成员变量声明区
  4. 内部接口声明区
  5. 初始化相关方法区
  6. 事件响应方法区
  7. 普通逻辑方法区
  8. 重载的逻辑方法区
  9. 发起异步任务方法区
  10. 异步任务回调方法区
  11. 生命周期回调方法区(出去onCreate()方法)
  12. 内部类声明区

3.4.2 类成员排列通用规则

  1. 按照发生的先后顺序排列
  2. 常量按照使用先后排列
  3. UI控件成员变量按照layout文件中的先后顺序排列
  4. 普通成员变量按照使用的先后顺序排列
  5. 方法基本上都按照调用的先后顺序在各自区块中排列
  6. 相关功能作为小区块放在一起(或者封装掉)

3.4.3 重载:永不分离

当一个类有多个构造函数,或是多个同名方法,这些函数/方法应该按顺序出现在一起,中间不要放进其它函数/方法。

4. 代码格式

4.1. 大括号(重要)

  • 大括号与if,else,for,do,while语句一起使用,即使只有一条语句(或是空),也应该把大括号写上。

  • 非空块:遵循 K&R 风格,如:

      if (a > b) {
          a = 9;
      } else {
          a = 10;
      }
    
  • 空块:可以使用简洁版本,如:

      void doNothing() {}
      void initData() {}
    

4.2. 块缩进:4个空格(重要)

每当开始一个新的块,缩进增加4个空格,当块结束时,缩进返回先前的缩进级别。缩进级别适用于代码和注释。

4.3. 列限制:80或100(建议)

一个项目可以选择一行80个字符或100个字符的列限制,除了下述例外,任何一行如果超过这个字符数限制,必须自动换行。
例外:

  • 不可能满足列限制的行(例如,Javadoc中的一个长URL,或是一个长的JSNI方法参考)。
  • package和import语句。
  • 注释中那些可能被剪切并粘贴到shell中的命令行。

4.4. 空白(建议)

4.4.1. 垂直空白

  1. 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。 例外: 两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。
  2. 在函数体内,语句的逻辑分组间使用空行。
  3. 类内的第一个成员前或最后一个成员后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。
  4. 多个连续的空行是允许的,但没有必要这样做。

4.4.2. 水平空白

  1. 分隔任何保留字与紧随其后前后的括号。

     for (int i = 0; i < 9; i++) {}
     while (i > 6) {}
    
  2. 算术运算符、关系运算符、逻辑运算符、赋值运算符的左右用一个空格分隔。

     int i = 0;
     a = 3 + 5;
     a > b;
    
  3. 如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。如:

     int price = 9999; // 商品的单价
    

4.4.3. 用小括号来限定组(推荐)

除非作者和reviewer都认为去掉小括号也不会使代码被误解,或是去掉小括号能让代码更易于阅读,否则我们不应该去掉小括号。
我们没有理由假设读者能记住整个Java运算符优先级表。

4.5. 具体结构

4.5.1. 枚举类

枚举常量间用逗号隔开,换行可选。
没有方法和文档的枚举类可写成数组初始化的格式:

private enum Weeks {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

4.5.2. 变量声明

  1. 每次只声明一个变量,不要使用组合声明,比如:

     // bad
     int a, b;
     
     // good
     int a;
     int b;
    
  2. 需要时才声明,并尽快进行初始化。

Tips:不要在一个代码块的开头把局部变量一次性都声明了(这是c语言的做法),而是在第一次需要使用它时才声明。 局部变量在声明时最好就进行初始化,或者声明后尽快进行初始化。

4.5.3. 数组

  1. 数组初始化:可写成块状结构,比如:

     new int[] {
     0, 1, 2, 3 
     }
     new int[] {
             0,
             1,
             2,
             3
     }
     new int[] {
             0, 1,
             2, 3
     }
     new int[]
             {0, 1, 2, 3}
    
  2. 非C风格的数组声明

中括号是类型的一部分:String[] args, 而非 String args[]

4.5.4. switch语句

  • 缩进

与其它块状结构一致,switch块中的内容缩进为2个空格。每个switch标签后新起一行,再缩进2个空格,写下一条或多条语句。

  • Fall-through:注释

在一个switch块内,每个语句组要么通过break, continue, return或抛出异常来终止,要么通过一条注释来说明程序将继续执行到下一个语句组, 任何能表达这个意思的注释都是OK的(典型的是用// fall through)。这个特殊的注释并不需要在最后一个语句组(一般是default)中出现。

示例:

switch (input) {
    case 1:
    case 2:
        prepareOneOrTwo();        // fall through
    case 3:
        handleOneTwoOrThree();
        break;
    default:
        handleLargeNumber(input);
}
  • default的情况要写出来

每个switch语句都包含一个default语句组,即使它什么代码也不包含。

4.5.5. 注解

注解紧跟在文档块后面,应用于类、方法和构造函数,一个注解独占一行。这些换行不属于自动换行(第4.5节,自动换行),因此缩进级别不变。

5. 命名约定(重要)

5.1. 包名

包名全部小写,连续的单词只是简单地连接起来,不使用下划线。

采用反域名命名规则,全部使用小写字母。一级包名为com,二级包名为xx(可以是公司或则个人的随便),三级包名根据应用进行命名,四级包名为模块名或层级名。

具体的约定需要根据采用的架构决定。

5.2. 类名

类名都以UpperCamelCase风格编写。
类名通常是名词或名词短语,接口名称有时可能是形容词或形容词短语。现在还没有特定的规则或行之有效的约定来命名注解类型。
名词,采用大驼峰命名法,尽量避免缩写,除非该缩写是众所周知的, 比如HTML,URL,如果类名称中包含单词缩写,则单词缩写的每个字母均应大写。

描述 例如
Activity类 Activity为后缀标识 欢迎页面类
WelcomeActivity
Adapter类 Adapter 为后缀标识 新闻详情适配器
NewDetailAdapter
解析类 Parser为后缀标识 首页解析类
HomePosterParser
工具方法类 Util或Manager为后缀标识(与系统或第三方的Utils区分)或功能+Util 线程池管理类:
ThreadPoolManager
日志工具类: LogUtil(Logger也可)
打印工具类:PrinterUtil
数据库类 以DBHelper为后缀标识 新闻数据库: NewDBHelper
Service类 以Service为后缀标识 时间服务: TimeService
BroadcastReceive类 以Broadcast为后缀标识 时间通知:TimeBroadcast
ContentProvider 以Provider为后缀标识
自定义的共享基础类 以Base开头 BaseActivity, BaseFragment

测试类的命名以它要测试的类的名称开始,以Test结束。
例如: HashTest

5.3. 接口(interface)

首字母大写,驼峰命名,使用名词。带I前缀,或able,ible,er等后缀,如IManager,OnClickListener

5.4. 方法命名

小驼峰法命名,方法名通常是动词或者动词短语

5.5. 常量命名

所有单词大写,单词间以”_“分隔

5.6. 变量命名

小驼峰命名。成员变量以m开头;静态变量以s开头

个人建议:Android开发中会使用很多UI控件,为了避免控件和普通的成员变量混淆,在java代码中的成员变量采用小驼峰命名,在xml布局文件中的控件采用全小写字母加下划线命名。如下:

// MainActivity.java中:
...
Button orderBtn;
orderBtn = (Button) findViewById(R.id.order_btn);
.
.
.

// activity_main.xml中:
...
<Button
    android:id="@+id/order_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
...

5.7. 资源文件

(1)布局文件

Type Format
Activity activity_
Fragment fragment_
Dialog dialog_
PopupWindow popup_
Menu menu_
Adapter layout_item_

(2)图片

Suffix Meaning
bg_xxx 背景图片
btn_xxx 按钮
ic_xxx 单个图标
bg_描述_状态 控件上的不同状态
btn_描述_状态 按钮上的不同状态
chx_描述_状态 选择框

(3)values

类别 命名 示例
strings 模块名_逻辑名称 main_menu_about
colors 模块名逻辑名称颜色 main_info_bg_gray
style 模块名_逻辑名称(驼峰法) main_tabBottom
dimen 模块名_逻辑名称 main_menu_padding

6. 通用规则(重要)

1、基础

  • 不要直接跨业务模块调用方法,一个模块提供一个对外类
  • 禁止将整个类格式化
  • 每个类长度尽量不要不超过1000行
  • 一行最多只能写一条语句,不允许一行定义多个变量或执行多条语句
  • 一个方法只做一件事情,方法体不能太长,且不能传入太多参数,一般5个以内,暂定不超过8个
  • 每行代码不超过100个字符,超过的需要使用缩进换行
  • 嵌套层数不应超过3层
  • 代码中禁止使用硬编码,把一些数字或字符串定义成常量
  • 用4个空格代替TAB符(缩进)
  • 恰当使用 TODO:FIXME:

2、异常处理

基本原则: Don't ignore exceptions. Don't catch generic exception.

  • 异常可以抛给上层处理、或在catch块中抛给上层处理、或直接在try/catch中打印并做出合理处理,如果catch块不做处理需要注明原因

  • 不要直接捕获通用Exception基类,对于已知的每种异常,catch中需要具体列出来,并对每种异常做出相应处理,形如:
    try/catch(JSONException e)/catch(Exception e)/finally,先catch已知异常再catch通用Exception

  • 在某些情况下,允许直接捕获Exception基类。如为了防止在UI或批处理任务中出现错误,可以在应用顶层加上try/catch(Exception e),但是需要注明原因

7. 注释(建议)

程序员在写代码的时候,注意为代码加注释,并以合理的格式为代码加注释,这样既方便别人查看代码,也方便自己以后查看。

7.1. 逐层注释

为每个代码块添加注释,并在每一层使用统一的注释方法和风格。例如:

针对每个类:包括摘要信息、作者信息、以及最近修改日期等;

针对每个方法:包括用途、功能、参数和返回值等。

在团队工作中,采用标准化的注释尤为重要。当然,使用注释规范和工具(例如C#里的XML,Java里的Javadoc)可以更好的推动注释工作完成得更好。

7.2. 使用分段注释

如果有多个代码块,而每个代码块完成一个单一任务,则在每个代码块前添加一个注释来向读者说明这段代码的功能。例子如下:

// Check that all data records are correct
foreach (Record record in records) {
    if (rec.checkStatus() == Status.OK) {
        ...
    }
}

// Now we begin to perform transactions
Context ctx = new ApplicationContext();
ctx.BeginTransaction();
...

7.3. 在代码行后添加注释

如果多行代码的每行都要添加注释,则在每行代码后添加该行的注释,这将很容易理解。例如:

const MAX_TIMES = 10; // maximum number of packets
const MASK = 0x1F;    // mask bit TCP

在分隔代码和注释时,有的开发者使用tab键,而另一些则使用空格键。然而由于tab键在各编辑器和IDE工具之间的表现不一致,因此最好的方法还是使用空格键。

7.4. 不要侮辱读者的智慧

避免以下显而易见的注释:写这些无用的注释会浪费你的时间,并将转移读者对该代码细节的理解。

if (a == 5) {        // if a equals 5
    counter = 0;    // set the counter to zero
}

7.5. 礼貌点

避免粗鲁的注释,如:“注意,愚蠢的使用者才会输入一个负数”或“刚修复的这个问题出于最初的无能开发者之手”。这样的注释能够反映到它的作者是多么的拙劣,你也永远不知道谁将会阅读这些注释,可能是:你的老板,客户,或者是你刚才侮辱过的无能开发者。

7.6. 关注要点

不要写过多的需要转意且不易理解的注释。避免ASCII艺术,搞笑,诗情画意,hyperverbosity的注释。简而言之,保持注释简单直接。

7.7. 使用一致的注释风格

一些人坚信注释应该写到能被非编程者理解的程度。而其他的人则认为注释只要能被开发人员理解就行了。无论如何,Successful Strategies for Commenting Code已经规定和阐述了注释的一致性和针对的读者。就个人而言,我怀疑大部分非编程人员将会去阅读代码,因此注释应该是针对其他的开发者而言。

7.8. 使用特有的标签

在一个团队工作中工作时,为了便于与其它程序员沟通,应该采用一致的标签集进行注释。例如,在很多团队中用TODO标签表示该代码段还需要额外的工作。

注释标签切忌不要用于解释代码,它只是引起注意或传递信息。如果你使用这个技巧,记得追踪并确认这些信息所表示的是什么。

int Estimate(int x, int y) {
    // TODO: implement the caculations
    return 0;
}

7.9. 在写代码时添加注释

在写代码时就添加注释,这时在你脑海里的是清晰完整的思路。如果在代码最后再添加同样注释,它将多花费你一倍的时间。而“我没有时间写注释”,“我很忙”和“项目已经延期了”这都是不愿写注释而找的借口。一些开发者觉得应该write comments before code,用于理清头绪。例如:

public void processOrder() {
    // make sure the products are available
    // check that the customer is valid
    // send the order to the store
    // generate bill
}

7.10. 为自己注释代码

当注释代码时,要考虑到不仅将来维护你代码的开发人员要看,而且你自己也可能要看。用Phil Haack大师的话来说就是:“一旦一行代码显示屏幕上,你也就成了这段代码的维护者”。因此,对于我们写得好(差)的注释而言,我们将是第一个受益者(受害者)。

附录

表1:UI控件缩写表

控件 缩写 例子
LinearLayout ll menuLL,toolBarLL
RelativeLayout rl menuRL, toolBarRL
FrameLayout fl mainFL, toolBarFL
TableLayout tl tabTL
Button btn homeBtn, searchBtn
ImageButton ibtn playIBtn
TextView tv nameTV, titleTV
EditText et usernameET, passwordET
ListView lv articleLV, carLV
ImageView iv headIV, logoIV
GridView gv photoGV
ProgressBar pb loadingPB
SeekBar sb scoreSB
RadioButton rbtn femaleRBtn
CheckBox cb acceptCB
ScrollView sv homeSV
Recyclerview rv articleRV
WebView wv newsDetailWV
Spinner spn citySPN
Top