Android编码规范
日期 | 作者 | 备注 |
2013年7月30日星期二 | 王明东 | 初稿 |
|
|
|
编写本规范的目的是为了进一步规范Android软件编程风格,提高软件源程序的可读性、可靠性和可重用性,确保在开发成员或开发团队之间的工作可以顺利交接,不必花很大的力气便能理解已编写的代码,以便继续维护和改进以前的工作,提高软件源程序的质量和可维护性,减少软件维护成本。
本规范的内容包括:注释、命名规则、等。
本规范分成规则性和建议性两种:对于规则性规范,要求所有软件开发人员严格执行;对于建议性规范,开发人员可以根据实际情况选择执行。
自本规范实施之日起,以后新编写的和修改的代码均应执行本规范。
本规范用到的术语解释如下:
规则:编程时必须遵守的规则。
建议:编程时必须加以考虑的原则。
说明:对相应规则的必要的解释。
正例:对此规则或建议给出的修正后的例子。
反例:对此规则或建议给出的反面的例子。
1 注释
注释有助于理解代码,有效的注释是指在代码的功能、意图层次上进行注释,提供有用、额外的信息,而不是代码表面意义的简单重复。
Java程序有两类注释:实现注释,文档注释。
1.1 实现注释
实现注释是那些在C++中见过的,使用/*…*/和//界定的注释。
【规则1-1-1】注释应与其描述的代码相近,对代码的注释应放在其上方(需与其上面的代码用空行隔开)或右方(对单条语句的注释)相邻位置,不可放在下面。 |
说明:在使用缩写时或之前,应对缩写进行必要的说明。避免在代码行的末尾添加注释;行尾注释使代码更难阅读。不过在批注变量声明时,行尾注释是合适的;在这种情况下,将所有行尾注释在公共制表位处对齐。代码如果超过行在中间加入关键部分的注释。
1.2 文档注释
文档注释(被称为“doc comments”)是Java独有的,并由/**…*/界定,注释内容里包含标签。文档注释可以通过javadoc工具转换成HTML文件。
实现注释用以注释代码或或者实现细节。文档注释从实现自由(implemtentation-free)的角度描述代码的规范。它可以被那些手头没有源码的开发人员读懂。
【规则1-2-1】注释使用中文注释。与doc有关的标准英文单词标签保留。 |
标签 | 用处 | 用途 |
@author作者的名称 | 类、接口 | 说明特定某一段程序代码的作者。每一个作者各有一个标记。 |
@deprecated | 类、方法 | 说明该类的应用程序编程接口 (API) 已被废弃,因此应不再使用。 |
@exception name description | 方法 | 说明由方法发出的异常。一个异常采用一个标记,并要给出异常的完整类名。 |
@param name 参数名的描述 | 方法 | 用来说明传递给一个方法的参数,其中包括参数的类型/类和用法。每个参数各有一个标记。 |
@return方法返回值的描述 | 方法 | 若方法有返回值,对该返回值进行说明。应说明返回值的类型/类和可能的用途。 |
@since | 类、方法 | 例如: sinceJDK 1.1:说明自从有 JDK 1.1 以来,该项已存在了多长时间。 |
@see 类名 | 类、接口、方法、字段 | 在文档中生成指向特定类的超文本链接。可以并且应该采用完全合法的类名。 |
@seeClassName#memberfunctionName | 类、接口、方法、字段 | 在文档中生成指向特定方法的超文本链接。可以并且应该采用完全合法的类名。 |
@version版本号 | 类、接口 | 说明特定一段代码的版本信息。 |
【规则1-2-2】类、接口头部必须进行注释。 |
说明:注释必须列出:类、名称、内容摘要等。
类编号由功能模块编号和类名两部分组成,中间用“_”隔开,功能模块编号使用该类所在
的功能模块的编号,类名用类的名称。例如:M01_Employee。
正例:
下面是类头部的中文注释:
/**
* 类名称
* 内容摘要//说明主要功能。
* @author UMK.XX
* @since 2013/07/30 修改说明
*/
|
【规则1-2-3】公共方法前面应进行文档型注释。 |
说明:注释必须列出:方法编号、主要功能、参数类型、输入参数、返回值、调用的前置条件和后置条件、异常说明、关键算法、可见性决策等。
正例:
下面是公共方法头部的注释:
/**
* 方法名称
* 方法摘要//说明主要功能。
* @author UMK.XX
* @since 2013/07/30 修改说明
* @param param1 参数一说明,
* @param param1 数据1
* @param param2 数据2
* @return 返回测试值 、意义(如:数值,1:成功,0:失败)
*/
〖建议1-2-1〗Java语言中,公共的属性采用单行文档注释,对于需要比较多的声明的,可进行多行注释。 |
说明:如果是Javadoc注释,属性可以采用文档型注释。
正例:
对于public型变量的单行声明:
/** classVar1 对classVar1的声明 */
public static int classVar1;
对于public型变量的多行声明:
/**
*classVar1 对classVar1的声明第一行
*对classVar1的声明第二行(继续对classVar1的声明)
*/
public static int classVar1;
对于public型变量的多行声明:
/**
*classVar1 对classVar1的声明第一行
*对classVar1的声明第二行(继续对classVar1的声明)
*/
public static int classVar1;
〖建议1-2-2〗通过对方法、变量等正确的命名以及合理地组织代码结构,使代码成为自注释的。 |
说明:清晰准确的方法、变量的命名,可增加代码的可读性。
〖建议1-2-3〗尽量避免在注释中使用缩写,特别是不常用缩写。 |
说明:在使用缩写时,应对缩写进行必要的说明。
2 命名规则
好的命名规则能极大地增加可读性和可维护性。同时,对于一个有上百个人共同完成的大项目来说,统一命名约定也是一项必不可少的内容。本章对程序中的所有标识符(包括包、变量名、控件名、常量名、方法名、类名、接口等)的命名做出约定。
三种命名规范说明:
Pascal规范:第1个字符大写,目标名中的每个单词的第1个字母也大写,比如InvoiceNumber或者PrintInvoice。其他的所有字符都小写。
Camel规范:第1个字符不大写,但目标名中的每个单词的第1个字母大写,比如,invoiceNumber。其他的所有字符都小写。
匈牙利规范:在目标名中加入表示类型的前缀,如strName。
2.1 结构命名
规约:
包命名必须以com.umk开始,后面跟有项目名称(或者缩写),再后面为模块名或层级名称。 如:com.umk.项目缩写.模块名 com.umk.nloc.bookmark
如:com.umk.项目缩写.层级名 com.umk.nloc.activities
2.2 方法&参数命名
【规则2-1】标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。 |
说明:标识符应当直观且可以拼读,可望文知义,避免使人产生误解。程序中的英文单词一般不要太复杂,用词应当准确。
【规则2-2】标识符只能由26个英文字母,10个数字及下划线的一个子集来组成,并严格禁止使用连续的下划线。用户定义的标识符下划线不能出现在标识符的头尾,数字也不能出现在标识符的头部。 |
说明:这样做的目的是为了使程序易读。因为variable_name和variable__name很难区分,下划线符号‘_’若出现在标识符头或结尾,容易与不带下划线‘_’的标识符混淆。
【规则2-3】标识符应当使用完整的英文描述,标识符的命名应当符合“min-length && max-information”原则,谨慎使用缩写。 |
说明:对于标识符应当使用完整的英文进行描述,对于整个描述较长的,可对单词进行缩略。较短的单词可通过去掉“元音”形成缩写,较长的单词可取单词的头几个字母形成缩写,一些单词有大家公认的缩写,常用单词的缩写必须统一。协议中的单词的缩写与协议保持一致。对于某个系统使用的专用缩写应该在某处做统一说明。设计命名中应该慎用缩写命名。如要采用,则应采用统一的缩略规则,并且在代码的相应部分统一采用缩写。例如,采用num作为number的缩写,那么在整个代码中应该始终使用该缩写。在使用缩写时,在使用缩写时,应该按着专业术语的要求大小写,例如,System.IO,而不是System.Io。
正例:如下单词的缩写能够被大家认可:
temp 可缩写为tmp;
flag 可缩写为flg;
statistic 可缩写为 stat ;
increment 可缩写为inc;
message 可缩写为msg;
以下是一些常用缩写:
常用词 | 缩写 |
Argument | arg |
Buffer | buf |
Clear | clr |
Clock | clk |
Compare | cmp |
Configuration | cfg |
Context | ctx |
Delay | dly |
Device | dev |
Disable | dis |
Display | disp |
Enable | en |
Error | err |
Function | fnct |
Hexadecimal | hex |
Initialize | init |
Mailbox | mbox |
Manager | mgr |
Maximum | max |
Message | msg |
Minimum | min |
Multiplex | mux |
operatingsystem | OS |
Parameter | param |
Previous | prev |
Priority | prio |
Read | rd |
Ready | rdy |
Register | reg |
Schedule | sched |
Semaphore | sem |
Stack | stk |
Synchronize | sync |
Timer | tmr |
Trigger | trig |
Write | wr |
【规则2-4】采用应用领域相关的术语来命名。 |
说明:软件开发人员应注意软件用户的一些约定术语,不应当随意的创造术语,这会降低软件的易用性。
【规则2-5】程序中不要出现仅靠大小写区分的相似的标识符。 |
说明:命名时应避免采用几乎相同的名称。例如,变量名称persistentObject和persistentObjects不应当同时运用;anSqlDatabase和anSQLDatabase也不应同时使用。
【规则2-6】用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。 |
说明:下面是一些在软件中常用的反义词组。
add / remove ; begin / end; create / destroy ;insert / delete;
first / last ; get / set; increment / decrement ; put / get;
add / delete; lock / unlock;open / close; min / max;
old / new ;start / stop;next / previous;source / target;
show / hide ; send / receive ;source / destination ;cut / paste ;
up / down
【规则2-7】类名和接口使用类意义完整的英文描述,每个英文单词的首字母使用大写、其余字母使用小写的大小写混合法。 |
正例:
OrderInformation,CustomerList
【规则2-8】属性名使用意义完整的英文描述,第一个单词的字母使用小写,剩余单词首字母大写其余字母小写的大小写混合法。属性名不能与方法名相同。 |
正例:
private String customerName;
private int orderNumber;
【规则2-9】方法名使用类意义完整的英文描述:第一个单词的字母使用小写、剩余单词首字母大写其余字母小写的大小写混合法。 |
正例:
privatevoidinsertCustomer();
【规则2-10】方法中,存取属性的方法采用set和 get方法,动作方法采用动词和动宾结构。 |
格式:
get+ 非布尔属性名()
is+ 布尔属性名()
set+ 属性名()
动词()
动词 + 宾语()
正例:
public String getType();
publicbooleanisFinished();
public void setVisible(boolean);
public void show();
public voidaddKeyListener(Listener);
【规则2-11】常量名使用全大写的英文描述,英文单词之间用下划线分隔开,并且使用 static final修饰。 |
正例:
publicstaticfinal int MAX_VALUE = 1000;
〖建议2-12〗非静态,非公开成员变量 添加m前缀,以便于同参数,变量相区分。 |
正例:
private String mCustomerName;
private int mOrderNumber;
〖建议2-13〗静态成员变量 添加s前缀。 |
正例:
private static String sCustomerName;
private static int sOrderNumber;
〖建议2-14〗常用组件类的命名以组件名加上组件类型名结尾。 |
正例:
Application类型的,命名以App 结尾——MainApp
Frame类型的,命名以Frame 结尾——TopoFrame
〖建议2-4〗含有集合意义的属性命名,尽量包含其复数的意义。 |
正例:
customers, orderItems
〖建议2-15〗变量声明应该只放在代码段的开始部分。最好不要到使用时才声明变量。对象类变量在函数体结束后,手工设置为null值,以利于资源的回收。 |
2.3 Android命名
2.3.1 layout命名
规约:layout xml 的命名必须以 全部单词小写,单词间以下划线分割,并且使用名词或名词词组,即使用 模块名_功能名称 来命名。
如:knowledge_gained_main.xmlà正确
如:list_book.xmlà错误!
1.contentview命名:activity_功能模块.xml
例如:activity_main.xml、activity_more.xml
2.Dialog命名:dialog_描述.xml
例如:dlg_hint.xml
2.PopupWindow命名:ppw_描述.xml
例如:ppw_info.xml
3. 列表项命名listitem_描述.xml
例如:listitem_city.xml
4.包含项:include_模块.xml
例如:include_head.xml、include_bottom.xml
2.3.2 id 命名
〖建议2-3-2〗layout中的id采用以下命名模式: view缩写_模块名称_view的逻辑名称 |
说明:view的缩写详情如下
ListView: lv
Button: btn
正例:
@+id/lv_appstore_applist
反例:
@+id/ListView01
2.3.3 资源命名
规约:layout中所使用的所有资源(如drawable,style等)命名必须以全部单词小写,单词间以下划线分割,并且尽可能的使用名词或名词组,即使用前缀_模块_描述 来命名。如果为公共资源,如分割线等,则直接用描述来命名
如:menu_icon_navigate.png à正确
如:某分割线:line.png 或 separator.png à正确
1. 静态图片前缀_模块、前缀_模块_描述
例如:bg_main.png、ic_main_search.png
2. 动态图片前缀_模块_描述_状态、前缀_描述_状态
例如:btn_film_buy_n.png、btn_film_buy_p.png、btn_back_n.png
如果有多种形态如按钮等除外如btn_film_buy.xml(selector)
2.3.4 资源缩写说明
前缀 | 说明 |
ic --icon | 主要用于布局和子布局的图标 |
bg--background | 主要用于布局和子布局的背景 |
di--divider | 主要用于分隔线,不仅包括Listview中的还包括普通布局中的线 |
sl--selector | 主要用于某一view多种状态,listview 按钮等 |
cl--color | 主要用于颜色值 |
bt--button | 主要用于按钮的表示,有时我们会在ic和bt之间犹豫,简单的区分即是功能视图,如果一个view执行的时back或者confirm或者cancel的功能,则命名上则应该使用bt |
后缀 | 说明 |
unit | 在使用xml的tilemode来配图片时,element图片使用此后缀 |
nor | 图片的状态,代表普通状态 |
hl | 图片的状态,代表高亮状态 |
press | 图片的状态,代表按下状态 |
select | 图片的状态,代表其所占的view被选中 |
unselect | 图片的状态,代表其所占的view没有被选中 |
组件名称对照表
组件名称 | 简写 | 组件名称 | 简写 |
Button | Btn(btn) | RadioButton | Rbtn(rbtn) |
ImageButton | Ibtn(ibtn) | TextView | Tv(tv) |
ImageView | Iv (iv) | ListView | Lv(lv) |
ProgressBar | Pbar(pbar) | EditText | Edtv(et) |
ScrollView | Sclv(scly) | CheckBox | Chk(chk) |
RelativeLayout | Rlyt(rlyt) | LinearLayout | Llyt(llyt) |
TableLayout | Tlyt(tlyt) | AbsoluteLayout | ALyt(alyt) |
FrameLayout | Flyt(flyt) |
|
|
2.3.5 字符串命名
最好不要跟title、dialog、button等东西关系起来,直接用相同英文含义就可以了,如果下:
<stringname="ok">确定</string>
<string name="welcome_to_use">欢迎使用</string>
再举一些不好的例子,如下<string name="menu_ok ">确定</string>
2.3.6 控件命名
控件缩写_描述
例如:TextViewtv_name、Button btn_buy、LinearLayoutllyt_body;
2.3.7 类命名
功能模块Activity.class 功能模块Service.class
如:MainActivity.class HuaFuBaoActivity.classAppUpgradeService.class
3 Android建议
Android下的开发基本可沿用Java下的编码规范,这里只是一些命令规范和日志的使用做一些补充。
〖建议3-1〗使用日志时,不重要的信息定义在debug等级或者info等级,较为严重的情况把日志定义的warn等级和error等级。正常情况下尽量不使用System.out.println();作为日志的输出 |
〖建议3-2〗数据一定要效验,例如 字符型转数字型,如果转换失败一定要有缺省值; 服务端响应数据是否有效判断 |
〖建议3-3〗如果所开发的为通用组件,为避免冲突,将drawable/layout/menu/values目录下的文件名增加前缀 |
〖建议3-4〗使用button+activitgroup实现tab效果时,使用Button.setSelected(true),确保按钮处于选择状态,并使activitygroup的当前activity与该button对应 |
〖建议3-5〗如果多个Activity中包含共同的UI处理,那么可以提炼一个CommonActivity,把通用部分叫由它来处理,其他activity只要继承它即可 |
〖建议3-6〗activity中在一个View.OnClickListener中处理所有的逻辑 |
〖建议3-7〗不要重用父类的handler,对应一个类的handler也不应该让其子类用到,否则会导致message.what冲突 |
〖建议3-8〗单元测试(逻辑测试、界面测试) |
〖建议3-9〗Log(系统名称 模块名称 接口名称,详细描述) |
〖建议3-10〗使用静态变量方式实现界面间共享要慎重 |
〖建议3-11〗引用第三方库要慎重,避免应用大容量的第三方库,导致客户端包非常大 |
〖建议3-12〗服务端可以实现的,就不要放在客户端 |
〖建议3-13〗styles.xml:将layout中不断重现的style提炼出通用的style通用组件,放到styles.xml中; |
〖建议3-12〗服务端可以实现的,就不要放在客户端 |
〖建议3-12〗服务端可以实现的,就不要放在客户端 |
****************************************
以下内容和规范无关
****************************************
4 Eclipse配置方法
1. 注释模板
在eclipse 的preferences 中,选择 java à code style àcode Template
1. 添加文件创建日志模板
2.设置类注释模板
2. 导入方法
在eclipse 的preferences 中,选择 java à code style àcode Template 中选择 Import,选择附件中的文件。
但是注意修改类注释和文件注释的作者名称为自己的!
3. 格式化模板
在eclipse 的preferences 中,选择 java à code style àformatter 中选择 Import,选择附件中的文件。
4. XML格式化
在eclipse 的preferences 中,选择 xml à xml files à xml editor 中 做如下设置
5 21种代码的坏味道
应该在编程中尽量避免这21种“坏味道”。
1. Duplicated Code
代码重复几乎是最常见的异味了。他也是Refactoring 的主要目标之一。代码重复往
往来自于copy-and-paste 的编程风格。
2. Long method
它是传统结构化的“遗毒“。一个方法应当具有自我独立的意图,不要把几个意图
放在一起。
3. Large Class
大类就是你把太多的责任交给了一个类。这里的规则是One Class One Responsibility。
4. Divergent Change
一个类里面的内容变化率不同。某些状态一个小时变一次,某些则几个月一年才变一次;某些状态因为这方面的原因发生变化,而另一些则因为其他方面的原因变一次。面向对象的抽象就是把相对不变的和相对变化相隔离。把问题变化的一方面和另一方面相隔离。这使得这些相对不变的可以重用。问题变化的每个方面都可以单独重用。这种相异变化的共存使得重用非常困难。
5. Shotgun Surgery
这正好和上面相反。对系统一个地方的改变涉及到其他许多地方的相关改变。这些变化率和变化内容相似的状态和行为通常应当放在同一个类中。
6. Feature Envy
对象的目的就是封装状态以及与这些状态紧密相关的行为。如果一个类的方法频繁用get 方法存取其他类的状态进行计算,那么你要考虑把行为移到涉及状态数目最多的那个类。
7. Data Clumps
某些数据通常像孩子一样成群玩耍:一起出现在很多类的成员变量中,一起出现在许多方法的参数中…..,这些数据或许应该自己独立形成对象。
8. Primitive Obsession
面向对象的新手通常习惯使用几个原始类型的数据来表示一个概念。譬如对于范围,他们会使用两个数字。对于Money,他们会用一个浮点数来表示。因为你没有使用对象来表达问题中存在的概念,这使得代码变的难以理解,解决问题的难度大大增加。好的习惯是扩充语言所能提供原始类型,用小对象来表示范围、金额、转化率、邮政编码等等。
9. Switch Statement
基于常量的开关语句是OO 的大敌,你应当把他变为子类、state 或strategy。
10.Parallel Inheritance Hierarchies
并行的继承层次是shotgun surgery 的特殊情况。因为当你改变一个层次中的某一个类时,你必须同时改变另外一个层次的并行子类。
11.Lazy Class
一个干活不多的类。类的维护需要额外的开销,如果一个类承担了太少的责任,应当消除它。
12.Speculative Generality
一个类实现了从未用到的功能和通用性。通常这样的类或方法唯一的用户是testcase。不要犹豫,删除它。
13.Temporary Field
一个对象的属性可能只在某些情况下才有意义。这样的代码将难以理解。专门建立一个对象来持有这样的孤儿属性,把只和他相关的行为移到该类。最常见的是一个特定的算法需要某些只有该算法才有用的变量。
14.Message Chain
消息链发生于当一个客户向一个对象要求另一个对象,然后客户又向这另一对象要求另一个对象,再向这另一个对象要求另一个对象,如此如此。这时,你需要隐藏分派。
15.Middle Man
对象的基本特性之一就是封装,而你经常会通过分派去实现封装。但是这一步不能走得太远,如果你发现一个类接口的一大半方法都在做分派,你可能需要移去这个中间人。
16.Inappropriate Intimacy
某些类相互之间太亲密,它们花费了太多的时间去砖研别人的私有部分。对人类而言,我们也许不应该太假正经,但我们应当让自己的类严格遵守禁欲主义。
17.Alternative Classes with Different Interfaces
做相同事情的方法有不同的函数signature,一致把它们往类层次上移,直至协议一致。
18.Incomplete Library Class
要建立一个好的类库非常困难。我们大量的程序工作都基于类库实现。然而,如此广泛而又相异的目标对库构建者提出了苛刻的要求。库构建者也不是万能的。有时候我们会发现库类无法实现我们需要的功能。而直接对库类的修改有非常困难。这时候就需要用各种手段进行Refactoring。
19.Data Class
对象包括状态和行为。如果一个类只有状态没有行为,那么肯定有什么地方出问题了。
20.Refused Bequest
超类传下来很多行为和状态,而子类只是用了其中的很小一部分。这通常意味着你的类层次有问题。
21.Comments
经常觉得要写很多注释表示你的代码难以理解。如果这种感觉太多,表示你需要Refactoring。