资源描述
写在开头:强制遵循的规范使用绿色标注,必须不允许的规范使用红色标注,建议遵循的使用紫色标注。
使用Android studio作为集成开发环境
对于编辑器,每个人都有自己的选择,让编辑器根据工程结构和构建系统高效运作,是每个人的责任。
推荐使用Android Studio,由谷歌开发,并且最接近Gradle,默认使用最新的工程结构。相比较而言Eclipse ADT使用旧的工程结构和Ant作为构建系统,它不仅需要繁琐的配置,而且gardle和adb命令行同样需要学习成本。
使用Gradle构建项目
默认编译环境使用Gradle。Ant不仅有限制而且操作方式非常繁琐,使用Gradle编译,可以轻松实现以下几点:
1. 构建App的不同版本,在debug和release之间轻松切换。
2. 快速制作简单的脚本任务
3. 轻松下载和管理依赖库
4. 能够方便的按照要求定制Keystore
另外值得一提的是,如果你想更快的构建小规模可重用程序模块,可以采用Facebook Buck,与传统Android编译工具相比,Buck凭借多核及并行技术,极大加速了Android工程的编译速度,并且在多次编译中,它会对未变动的模块进行标记,从而以增量式编译的方式进一步提高构建速度。
项目结构
废弃过时的Ant & Eclipse ADT工程结构,统一使用新的Gradle & Android Studio的工程结构。
要使用Android studio结构:
1. new-link-structure
2. ├─ library-imsdk
3. ├─ app
4. │ ├─ libs
5. │ ├─ src
6. │ │ ├─ androidTest
7. │ │ │ └─ java
8. │ │ │ └─ com/im/project
9. │ │ └─ main
10. │ │ ├─ java
11. │ │ │ └─ com/im/project
12. │ │ ├─ res
13. │ │ └─ AndroidManifest.xml
14. │ ├─ build.gradle
15. │ └─ proguard-rules.pro
16. ├─ build.gradle
17. └─ settings.gradle
不使用Eclipse结构:
1. old-link-structure
2. ├─ assets
3. ├─ libs
4. ├─ res
5. ├─ src
6. │ └─ com/im/project
7. ├─ AndroidManifest.xml
8. ├─ build.gradle
9. ├─ project.properties
10. └─ proguard-rules.pro
通过比较可见Android studio的项目结构更加清晰,强调了Gradle概念。其中library-imsdk是app所依赖的module。
签名配置
发布release版本的时候,必须确认SigningConfigs的保密性:
创建一个不加入版本控制系统的gradle.properties文件,或者记录在本地的local.properties中。
1. KEYSTORE_PASSWORD=storePassword
2. KEY_PASSWORD=keyPassword
上面提到的两个文件会被gradle自动引入,因此可以在buld.gradle中直接引用,例如:
1. signingConfigs {
2. release {
3. try {
4. storeFile file("myapp.keystore")
5. storePassword KEYSTORE_PASSWORD
6. keyAlias "storeKey"
7. keyPassword KEY_PASSWORD
8. }
9. catch (ex) {
10. throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
11. }
12. }
13. }
不采用以下示例方式,因为这会导致敏感信息的泄露:
1. signingConfigs {
2. release {
3. storeFile file("myapp.keystore")
4. storePassword "storePassword"
5. keyAlias "storeKey"
6. keyPassword "keyPassword"
7. }
8. }
使用Maven依赖代替jar包导入
如果在项目中明确使用jar文件,那么它们可能成为永久的版本,如2.1.1。而且很容易陷入频繁更新jar包的繁琐工作中,Maven很好的解决了这个问题,这也是 Gradle构建中推荐的使用方式,例如:
1. dependencies {
2. /*ReactiveX Library*/
3. compile 'io.reactivex:rxjava:1.1.3'
4. compile 'io.reactivex:rxandroid:1.1.0'
5. }
或者,在不确定该library最新版本的前提下,可以指定版本范围,如2.1.+,随后Gradle会优先加载Maven仓库中该library的最新版本。
命名规范
Java文件
参考源码命名规则,对Java类文件使用驼峰命名法。
对于继承自Android组件的类来说,类名应该以这个组件的类型作后缀,如:SignInActivity, UserProfileFragment, ImageUploaderService, ChangePasswordDialog
XML文件
资源等.xml文件要采用小写字母_下划线的组合形式。
Drawable相关
· Drawable文件建议命名方式:
Asset Type
Prefix
Example
Action bar
ab_
ab_stacked.9.png
Button
btn_
btn_send_pressed.9.png
Dialog
dialog_
dialog_top.9.png
Divider
divider_
divider_horizontal.9.png
Icon
ic_
ic_star.png
Menu
menu_
menu_submenu_bg.9.png
Notification
notification_
notification_bg.9.png
Tabs
tab_
tab_pressed.9.png
· icon文件建议命名方式:
Asset Type
Prefix
Example
Icons
ic_
ic_star.png
Launcher icons
ic_launcher
ic_launcher_calendar.png
Menu icons and Action Bar icons
ic_menu
ic_menu_archive.png
Status bar icons
ic_stat_notify
ic_stat_notify_msg.png
Tab icons
ic_tab
ic_tab_recent.png
Dialog icons
ic_dialog
ic_dialog_info.png
· selector states文件建议命名方式:
State
Suffix
Example
Normal
_normal
btn_order_normal.9.png
Pressed
_pressed
btn_order_pressed.9.png
Focused
_focused
btn_order_focused.9.png
Disabled
_disabled
btn_order_disabled.9.png
Selected
_selected
btn_order_selected.9.png
Layout相关
· Layout文件建议命名方式:
布局文件应该与Android组件的命名相匹配,以组件类型作为前缀,并且能够清晰的表达意图。例如:如果为SignInActivity创建一个布局文件,那么应该命名为activity_sign_in.xml。建议采用以下基本命名规则:
Component
Class Name
Layout Name
Activity
UserProfileActivity
activity_user_profile.xml
Fragment
SignUpFragment
fragment_sign_up.xml
Dialog
ChangePasswordDialog
dialog_change_password.xml
AdapterView item
item_person.xml
Partial layout
partial_stats_bar.xml
值得一提的是,一些布局文件需要通过Adapter填充,如ListView,Recyclerview等列表视图,这种场景下,布局的命名应该以item_作为前缀。另外还有一种比较常见的情况,一个布局文件作为另一个布局文件的一部分而存在,或者使用了include,merge等标签的布局,建议使用partial_、include_或者merge_作为前缀,这一类布局的命名同样应该清晰的表达其意图。
· Id命名方式:
控件Id的命名应该以该控件类型的缩写作为前缀,同样要使用小写字母_下划线的组合形式,能够清晰表达意图是命名的前提:
Element
Prefix
TextView
tv_
ImageView
iv_
Button
btn_
Menu
menu_
示例如下:
1. <ImageView
2. android:id="@+id/iv_profile"
3. android:layout_width="wrap_content"
4. android:layout_height="wrap_content" />
1. <menu>
2. <item
3. android:id="@+id/menu_done"
4. android:title="Done" />
5. </menu>
Color相关
colors.xml可以比喻成“调色板”,只映射ARGB值,不应该存在其他类型的数值,更不要使用它为不同的按钮来定义ARGB。
应该根据颜色或者风格对ARGB赋值,要使用基色_ARGB的命名规则:
1. <resources>
2. <color name="white_FFFFFF">#FFFFFF</color>
3. <color name="gray_DBDBDB">#DBDBDB</color>
4. <color name="gray_939393">#939393</color>
5. <color name="gray_5F5F5F">#5F5F5F</color>
6. <color name="black_323232">#323232</color>
7. <color name="green_27D34D">#27D34D</color>
8. </resources>
值得一提的是,这样规范的颜色很容易修改或重构,App所使用的颜色数量和种类也会变得非常清晰。
相反地,不使用以下对色值的命名规则:
1. <resources>
2. <color name="button_foreground">#FFFFFF</color>
3. <color name="button_background">#2A91BD</color>
4. <color name="comment_background_inactive">#5F5F5F</color>
5. <color name="comment_background_active">#939393</color>
6. <color name="comment_foreground">#FFFFFF</color>
7. <color name="comment_foreground_important">#FF9D2F</color>
8. ...
9. <color name="comment_shadow">#323232</color>
使用这种定义方式,我们需要非常的谨慎,一不小心就会重复定义ARGB,而且当改变基本色时,会造成毫无意义的重复操作。
Dimen相关
我们应该像对待colors.xml一样对待dimens.xml文件,与定义“调色板”无异,同样应该为字体定义一块“字号版”。
要使用以dimen_作为前缀的命名规范:
1. <resources>
2. <!--SP-->
3. <dimen name="dimen_22sp">22sp</dimen>
4. <dimen name="dimen_18sp">18sp</dimen>
5. <dimen name="dimen_15sp">15sp</dimen>
6. <!--DP-->
7. <dimen name="dimen_60dp">60dp</dimen>
8. <dimen name="dimen_40dp">40dp</dimen>
9. <dimen name="dimen_32dp">32dp</dimen>
10. </resources>
这样写的好处是,使组织结构和修改布局风格变得非常容易,同时也避免了对重复数值的定义。
String相关
对string的声明必须添加业务区分标识,前缀应该能清楚地表达功能职责,如,registration_email_hint,registration_name_hint。如果一个Sting不属于任何模块,就意味着它是通用的,遵循以下规范:
Prefix
Description
error_
错误提示
msg_
一般信息提示
title_
标题提示,如,Dialog标题
action_
动作提示,如,“保存”,“取消”,“创建”
另外,需要注意的是,所有用作UI展示的字符串都必须定义在string.xml文件中,不准出现在java或者layout布局中
Style与Theme相关
Style与Theme的命名统一使用驼峰命名法。请谨慎使用style与theme,避免重复冗余的文件出现。可以出现多个styles.xml 文件,如:styles.xml,style_home.xml,style_item_details.xml,styles_forms.xml等。
另外值得一提的是:res/values目录下的文件可以任意命名,但前提是该文件能够明确表达职责所属,因为起作用的并不是文件本身,而是内部的标签属性。
编码规范与指导方针
XML文件规范
属性排序
对于如何排版一个布局文件,建议编码规范如下:
· 每个属性独占一行,缩进四个空格
· android:id作为第一个属性存在
· 如果存在style属性,则紧随id之后
· 如果不存在style属性,则android:layout_****紧随id之后
· 当布局中的一个元素不再包含子元素时,另起一行,使用自闭合标签/>,方便调整和添加新的属性
xml示例如下:
1. <?xml version="1.0" encoding="utf-8"?>
2. <LinearLayout xmlns:android="
3. xmlns:tools="
4. android:layout_width="match_parent"
5. android:layout_height="match_parent"
6. android:orientation="vertical"
7. >
8. <TextView
9. android:id="@+id/name"
10. style="@style/FancyText"
11. android:layout_width="match_parent"
12. android:layout_height="wrap_content"
13. android:layout_alignParentRight="true"
14. android:text="@string/name"
15. />
16. <include layout="@layout/reusable_part" />
17. </LinearLayout>
使用tools标签预览视图
· 布局预览建议使用tools:****相关属性,避免android:text="Hard code"等硬编码的出现,具体可参考Designtime attributes。
要使用如下示例方式设置属性:
1. <TextView
2. android:layout_width="wrap_content"
3. android:layout_height="wrap_content"
4. tools:text="Home Link" />
不要使用以下硬编码方式:
1. <TextView
2. android:layout_width="wrap_content"
3. android:layout_height="wrap_content"
4. android:text="Home Link" />
通用style
值得一提的是,android:layout_****属性应该在XML中定义,同时其它属性android:****应放在style中。核心准则是保证Layout属性(position, margin, size等)和content属性在布局文件中,同时将所有的外观细节属性(color, padding, font)放在style文件中。
另外,在上面提到的准则中,有以下几点需要注意:
· android:id明显应该在layout文件中
· Layout文件中的android:orientation属性对于一个LinearLayout布局来说,更具有意义
· 由于使用android:text定义内容,所以这个属性应该放在Layout文件中
· 有时候将android:layout_width和android:layout_height属性放到一个style.xml中作为一个通用的风格更有意义,但是默认情况下把这些属性放到Layout文件中比放到style.xml文件中更加直观。
· 只有通用组件使用style,其他组件不必使用。
避免层级冗余的嵌套
Layout结构优化方面,应尽量避免深层次的布局嵌套,这不仅会引发性能瓶颈,还会带来项目维护上的麻烦。在书写布局之前应该对ViewTree充分的分析,善用<merge>标签减少层级嵌套,或者使用Hierarchy Viewer等UI优化工具对Layout进行分析与优化。可参考Optimizing Your UI与Optimizing Layout Hierarchies。
Java代码规范
代码注释
创建class文件时,文件头部必须添加作者的全称拼音,创建时间以及对本类的描述。创建方法函数时,被public声明的函数,必须添加注释,除getXXXX()和setXXXX()等简单函数外,可不必在方法头中添加注释,但需要对所操作的字段添加描述,这里提供一个注释样板,可供参考:
1. /**
2. * 创建时间:2016/7/18 15:46 <br>
3. * 作者: dengwei <br>
4. * 描述: HTTP响应结果,状态码在[200..300)区间之外的异常信息包装类<br>
5. */
6. public final class HttpException extends Exception {
7. //HTTP状态码
8. private final int code;
9. //HTTP状态行信息
10. private final String message;
11. //HTTP完成响应
12. private final transient Response<?> response;
13.
14. public HttpException(Response<?> response) {
15. super("HTTP " + response.code() + " " + response.message());
16. this.code = response.code();
17. this.message = response.message();
18. this.response = response;
19. }
20. /**
21. * 获取HTTP响应异常时的状态码,状态码的值在[200..300)区间外
22. *
23. * @return HTTP响应状态码
24. */
25. public int code() {
26. return code;
27. }
28. /**
29. * 获取HTTP响应异常时的状态行信息,如果状态信息未知,返回值可能等与null。
30. *
31. * @return HTTP响应状态行信息
32. */
33. public String message() {
34. return message;
35. }
36. /**
37. * 获取完整的HTTP响应,可以从这里获取更多的响应信息。如果该异常已经被序列化了,返回值可能等于null。
38. *
39. * @return 完整的HTTP响应
40. */
41. public Response<?> response() {
42. return response;
43. }
44. }
另外值得一提的是,“TODO”和“FIXME”的注释要明确标明时间,作者和以及任务描述,如:
// TODO: 16/7/18 dengwei 优化文件检索和I/O读取速度
// FIXME: 16/7/18 dengwei 修复回调嵌套,避免回调地狱
文件头注释
关于文件头注释这里给出统一的模板和编写规则,务必声明创建该类的时间,作者以及对该类的功能描述
1.对于新创建的类,可以使用静态模板的方式,自动生成文件头,然后对其进行编写:
该模板如下:
/**
* 创建时间: ${YEAR}/${MONTH}/${DAY} ${HOUR}:${MINUTE} <br>
* 作者: ${USER} <br>
* 描述:
*/
2. 使用动态模板,重构已经存在的文件头:
该模板格式如下:
/**
* 创建时间: $date$ $time$ <br>
* 作者: $author$ <br>
* 描述:
*/
关于第二种方式,如在"abbreviation"一栏中填写的是“cc”,只需将光标移至文件头,输入“cc”后便会出现代码提示,点击回车或者Tab键,然后编写生成的文件头即可。
处理Exception
如果对异常没有任何处理方案,至少应该在catch代码块中添加打印异常逻辑e.printStackTrace(),不建议采用的不做任何处理的方式:
1. void setServerPort(String value) {
2. try {
3. serverPort = Integer.parseInt(value);
4. } catch (NumberFormatException e) { }
5. }
你可能认为这种异常的触发概率极低,或者认为即便发生,也不需要做着重的处理,因此在catch()代码块中不添加任何逻辑,这大大增加了定位异常的难度,这里的建议是尽量处理每一个异常,具体的处理逻辑依赖于所处的上下文场景,对异常的处理同样能够增加系统的弹性,让程序变更健壮。
同时,遇到异常应该列出所有最小集合,不建议使用类似genericHandler()的通用异常捕获逻辑:
1. try {
2. potentialIOExceptionFun(); // may throw IOException
3. potentialParsingExceptionFun(); // may throw ParsingException
4. potentialSecurityExceptionFun(); // may throw SecurityException
5. // phew, made it all the way
6. } catch (Exception e) { // I'll just catch all exceptions
7. genericHandler(); // with one generic handler!
8. }
应该在catch语句中对异常分别处理,或者调用日志上传、异常统计等API。
Field定义与命名规范
对Field的定义应该放在文件的首位,并且遵守以下规范:
· 非静态变量,以m作为前缀
· 静态变量,以s作为前缀
· 静态常量命名字母全部大写,单词之间用下划线分隔
示例:
1. public class MyClass {
2. public static final int SOME_CONSTANT = 42;
3. public int publicField;
4. private static MyClass sSingleton;
5. int mPackagePrivate;
6. private int mPrivate;
7. protected int mProtected;
8. }
名词缩写规范
Good
Bad
XmlHttpRequest
XMLHTTPRequest
getCustomerId
getCustomerID
String url
String URL
long id
long ID
注解使用规范
· @Override: 子类实现或者重写父类方法时,必须使用@Override对函数进行标注。
· @SuppressWarnings: 注解@SuppressWarnings应该用在消除那些明确不可能发生的警告上,示例如下:
1. //明确的类型安全(type-safe)转换
2. @SuppressWarnings("unchecked")
3. public static <R> FeedbackUseCase<R> createdUseCase() {
4. return (FeedbackUseCase<R>) new FeedbackUseCase();
5. }
6. //请先确保能够非常正确地使用Handler
7. @SuppressWarnings("handlerLeak")
8. private Handler mHandler = new Handler() {
9. @Override
10. public void handleMessage(Message msg) {
11. super.handleMessage(msg);
12. }
13. };
更多关于注解的使用技巧与规范请参考这里,或者使用android.support.annotation依赖包中的注解优化程序。
限制变量的作用域
定义变量时,应该保证变量所在域的最小化,这样做不仅能够增加代码的可读性和可维护性,还能有效减少出错的可能性。变量定义在尽可能小的域中,也是良好封装性的体现。
值得注意的是,变量应该在首次使用时被初始化,如果没有足够的条件来使用它,那么就应该延迟创建的时机,直到真正需要使用时再去进行初始化操作。
Log输出规范
使用Log类打印一些重要的信息对开发者而言是很重要的事情,切记不要使用Toast来做信息打印。
针对不同的信息使用对应的日志输出类型:
· Log.v(String tag, String msg) (verbose)
· Log.d(String tag, String msg) (debug)
· Log.i(String tag, String msg) (information)
· Log.w(String tag, String msg) (warning)
· Log.e(String tag, String msg) (error)
一般情况下,当前类名作为tag,并且使用static final修饰,当前类的首行,示例如下:
1. public class MyClass {
2. private static final String TAG = MyClass.class.getSimpleName();
3. public myMethod() {
4. Log.e(TAG, "My error message");
5. }
6. }
VERBOSE和DEBUG类型的Log不应该出现在Release版本中,INFORMATION、WARNING和ERROR类型的Log可以留下来,因为这些信息的输出能够帮助我们快速地定位问题所在,当然前提是,需要隐藏重要的信息输出,如,用户手机号,邮箱等。
只在Debug环境中输出日志的小技巧:
1. if (BuildConfig.DEBUG) Log.d(TAG, "The value of x is " + x);
类成员排序规范
关于这个并没有硬性要求,不过好的排序方式,能够提高可读性和易学性。这里给出一些排序建议:
1. 常量
2. 字段
3. 构造函数
4. 被重写的函数(不区分修饰符类型)
5. 被private修饰的函数
6. 被public修饰的函数
7. 被定义的内部类或者接口
示例如下:
1. public class MainActivity extends Activity {
2. private static final String TAG = MainActivity.class.getSimpleName();
3. private String mTitle;
4. private TextView mTextViewTitle;
5. @Override
6. public void onCreate() {
7. ...
8. }
9. private void setUpView() {
10. ...
11. }
12. public void setTitle(String title) {
13. mTitle = title;
14. }
15. static class AnInnerClass {
16. }
17. }
如果继承了Android组件,比如Activity或者Fragment,重写生命周期函数时,建议按照组件的生命周期进行排序,如:
1. public class MainActivity extends Activity {
2. @Override
3. public void onCreate() {}
4. @Override
5. public void onResume() {}
6. @Override
7. public v
展开阅读全文