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. 源文件结构
一个源文件包含(按顺序地):
- 许可证或者版权信息(如有需要)
- package语句
- import语句
- 一个顶级类(有且只有一个)的声明
以上每个部分之间用一个空行作为分割。
3.1. 许可证或版权信息
如果一个文件包含许可证或版权信息,那么它应当被放文件最前面。
3.2. package语句
package 语句不换行,列限制(4.4节)并不适用于package语句。(即package语句写在一行里)
3.3. import语句
- 不要使用通配符,即不要出现这样的语句:
import java.util*;
- 不要换行
- 分组
3.4. 类声明
类的成员顺序对易学性有很大的影响,但这也不存在唯一的通用法则。不同的类对成员的排序可能是不同的。
最重要的一点,每个类应该以某种逻辑去排序它的成员,维护者应该要能解释这种排序逻辑。比如, 新的方法不能总是习惯性地添加到类的结尾,因为这样就是按时间顺序而非某种逻辑来排序的。
3.4.1 区域划分(建议)
- 常量声明区
- UI控件成员变量声明区
- 普通成员变量声明区
- 内部接口声明区
- 初始化相关方法区
- 事件响应方法区
- 普通逻辑方法区
- 重载的逻辑方法区
- 发起异步任务方法区
- 异步任务回调方法区
- 生命周期回调方法区(出去onCreate()方法)
- 内部类声明区
3.4.2 类成员排列通用规则
- 按照发生的先后顺序排列
- 常量按照使用先后排列
- UI控件成员变量按照layout文件中的先后顺序排列
- 普通成员变量按照使用的先后顺序排列
- 方法基本上都按照调用的先后顺序在各自区块中排列
- 相关功能作为小区块放在一起(或者封装掉)
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. 垂直空白
- 类内连续的成员之间:字段,构造函数,方法,嵌套类,静态初始化块,实例初始化块。 例外: 两个连续字段之间的空行是可选的,用于字段的空行主要用来对字段进行逻辑分组。
- 在函数体内,语句的逻辑分组间使用空行。
- 类内的第一个成员前或最后一个成员后的空行是可选的(既不鼓励也不反对这样做,视个人喜好而定)。
- 多个连续的空行是允许的,但没有必要这样做。
4.4.2. 水平空白
-
分隔任何保留字与紧随其后前后的括号。
for (int i = 0; i < 9; i++) {} while (i > 6) {}
-
算术运算符、关系运算符、逻辑运算符、赋值运算符的左右用一个空格分隔。
int i = 0; a = 3 + 5; a > b;
-
如果在一条语句后做注释,则双斜杠(//)两边都要空格。这里可以允许多个空格,但没有必要。如:
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. 变量声明
-
每次只声明一个变量,不要使用组合声明,比如:
// bad int a, b; // good int a; int b;
-
需要时才声明,并尽快进行初始化。
Tips:不要在一个代码块的开头把局部变量一次性都声明了(这是c语言的做法),而是在第一次需要使用它时才声明。 局部变量在声明时最好就进行初始化,或者声明后尽快进行初始化。
4.5.3. 数组
-
数组初始化:可写成块状结构,比如:
new int[] { 0, 1, 2, 3 } new int[] { 0, 1, 2, 3 } new int[] { 0, 1, 2, 3 } new int[] {0, 1, 2, 3}
-
非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 |