原文链接:https://github.com/bufferapp/android-guidelines/blob/master/project_style_guidelines.md 英语溜的可去看看,此文章是跟公司的小伙伴一起翻译的
为了保持我们的代码的基本整洁和一致,在安卓工程从始至终我们都应该遵循一定的规范,这篇文档的目的就是为了定义这样一个工程指导的规范。
虽然很多事情可以通过Jenkins的代码质量检查来加强,但重要的是要注意那些没有被检查到的地方。
1. 工程指导
1.1 工程结构
当进行提交代码的工作时,工程应该保持如下的目录结构:
src/androidTest
src/test
src/commonTest
src/mainandroidTest - 功能测试目录
test - 单元测试目录
commonTest - 为AndroidTest & Test 提供的共享测试代码的目录
main - 应用代码目录
当你修改或者增加新功能的时候,工程结构依然应该保持如上的样子。
使用如上的工程结构可以让我们的应用代码从相关的测试代码中分离出来。
CommonTest目录使得我们的功能测试和单元测试可以共享部分代码,比如mock model creation and dagger test configuration classes.
1.2 文件命名
1.2.1 类文件
任何定义的类都应该使用驼峰命名格式,比如:
AndroidActivity, NetworkHelper, UserFragment, PerActivity
任何继承自android框架组建的类都应该使用组建名称来结尾,比如:
UserFragment, SignUpActivity, RateAppDialog, PushNotificationServer, NumberView
使用驼峰格式命名易于阅读。
用android框架组件名称结尾的继承类能够清楚的标识出类的用途
比如,如果要去查找修改RegistrationDialog时,那么这个命名使得定位这个类文件非常方便。
1.2.1 资源文件
当命名资源文件时,应该只使用小写字母和下划线替代空格,例如:
activity_main, fragment_user, item_post
在你查找布局文件时,这样命名同样使得定位文件很方便。
在使用android studio中,布局的包中的文件是按字母顺序排列的,这样意味着activity,fragment和其他的layout是按组分类放置的----所以你知道从哪里去开始找一个文件了。
不止如此,用组建名称开头的布局文件可以很清晰的标识这个布局文件是被那类组建使用。
1.2.2.1 Drawable 文件
Drawable resource files should be named using the ic_ prefix along with the size and color of the asset.
Drawable资源文件应该使用“ic_”前缀后跟上尺寸以及资源的颜色信息
For example, white accept icon sized at 24dp would be named:
比如,白色的24dp大小的用于接受动作的图标应该命名如下:
ic_accept_24dp_white
黑色用于取消动作的48dp大小的图标命名:
ic_cancel_48dp_black
我们使用这样的命名约定方便使用命名来组织drawable资源文件
如果命名中没有颜色和尺寸信息,那么开发者还需要打开drawable文件去查看这些信息。所以这样可以节约我们一点时间.
其他的drawable文件应该使用对应的前缀,如下:
Type
|
Prefix
|
Example
|
---|---|---|
Selector | selector_ | selector_button_cancel |
Background | bg_ | bg_rounded_button |
Circle | circle_ | circle_white |
Progress | progress_ | progress_circle_purple |
Divider | divider_ | divider_grey |
在Android Studio中这个命名约定依然可以帮助我们将相似的项组织到一起
并清晰的标识出这一文件用作什么
比如, 命令一个资源为“button_cancel”不能标识任何信息
这是一个selector资源呢还是一个圆角按钮背景呢?
正确的命名可以去除所有的引起的模棱两可.
在创建selector不同状态资源时,也应该使用对应的命名下标:
State
|
Suffix
|
Example
|
---|---|---|
Normal | _normal | btn_accept_normal |
Pressed | _pressed | btn_accept_pressed |
Focused | _focused | btn_accept_focused |
Disabled | _disabled | btn_accept_disabled |
Selected | _selected | btn_accept_selected |
使用如上清晰的下标绝对明显的标识出了selector状态资源的作用。
为有颜色和其他标识的资源文件增加下标,使得开发者在打开selector文件时就可以知道不同的selector资源的状态是什么了
1.2.2.2 Layout 文件
在命名布局文件时,应该用android组建的名称作为文件名的前缀,比如:
Component
|
Class Name
|
Layout Name
|
---|---|---|
Activity | MainActivity | activity_main |
Fragment | MainFragment | fragment_main |
Dialog | RateDialog | dialog_rate |
Widget | UserProfileView | view_user_profile |
AdapterView Item | N/A | item_follower |
注意:如果要创建的布局文件是有多个不同组件使用的,那么应该使用”layout_”前缀
这样不仅在层级目录中可以很方便的找到文件,
这样也可以帮助我们定义相应的这个layout布局文件归属的类。
1.2.2.3 Menu Files
菜单文件不需要使用“menu_”前缀。
在资源目录下已经有菜单包了,所以这是不必要的。
1.2.2.4 Values Files
所有的资源文件名必须是复数的,比如:
attrs.xml, strings.xml, styles.xml, colors.xml, dimens.xml
2. 代码指导
2.1 Java语句规则
2.1.1 绝不要忽略exceptions
正确的方式是避免没有处理的异常,如下:
public void setUserId(String id) {
try {
mUserId = Integer.parseInt(id);
} catch (NumberFormatException e) { }
}
如果这里出现问题,这里不会打印出任何信息,而且也很难debug,只能让人迷惑。
当catch一个异常时,我们总是需要输出error日志到控制台,用于调试,如果必要的话,需要警告用户这个异常。比如:
<!-- lang:java-->
public void setCount(String count) {
try {
count = Integer.parseInt(id);
} catch (NumberFormatException e) {
count = 0;
Log.e(TAG, "There was an error parsing the count " + e);
DialogFactory.showErrorMessage(R.string.error_message_parsing_count);
}
}
这里我们有如下的方式处理出错:
1. 显示一个信息给用户,提示他们这里出错了
2. 设置了一个可能的默认值
3. 抛出了一个合适的异常
2.1.2 绝对不要catch一般的异常:
public void openCustomTab(Context context, Uri uri) {
Intent intent = buildIntent(context, uri);
try {
context.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "There was an error opening the custom tab " + e);
}
}
为什么不要这样做呢?
在大部分情况下,catch这个通用的Exception或者Throwable都是不合适的。(特别是THrowable因为它包含Error的异常。)
这样意味着你没有期望到的异常(包括RuntimeExceptions像CLassCastException)都被抓获并在应用层进行error处理,这样是很危险的。
如果有人在你调用的代码里添加了一个新的类型的异常,编译器不会帮你认识到你需要处理这个不同的错误类型。这是你代码里很难发现的错误处理方式。
大多数情况下,你不应该用相同的处理方式来处理不同的exception.
如下,catch期望的异常并合适的处理:
public void openCustomTab(Context context, Uri uri) {
Intent intent = buildIntent(context, uri);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "There was an error opening the custom tab " + e);
}
}
2.1.3 组织 exceptions
在异常运行相同的代码的地方,他们应该增加可读性和避免代码复制。比如,你可能会像如下这样处理异常:
public void openCustomTab(Context context, @Nullable Uri uri) {
Intent intent = buildIntent(context, uri);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "There was an error opening the custom tab " + e);
} catch (NullPointerException e) {
Log.e(TAG, "There was an error opening the custom tab " + e);
} catch (SomeOtherException e) {
// Show some dialog
}
}
你可以这样做:
public void openCustomTab(Context context, @Nullable Uri uri) {
Intent intent = buildIntent(context, uri);
try {
context.startActivity(intent);
} catch (ActivityNotFoundException e | NullPointerException e) {
Log.e(TAG, "There was an error opening the custom tab " + e);
} catch (SomeOtherException e) {
// Show some dialog
}
}
2.1.4 Using try-catch over throw exception
在exception出现的地方使用try-catch块增加代码可读性
在代码中error发生的地方就处理,这样不管是debug还是更改error处理都很容易。
2.1.5 不要使用垃圾回收器
不能保证什么时候垃圾回收器会被调用,也不能保证垃圾回收器将在什么时候调用.
大部分情况下,在有良好的异常处理的情况下,你可以做你需要做的。
如果你实在需要,定义一个close()方法(或者类似的)并在方法调用时严格证明(毛线意思啊)
查看InputStreamfor作为例子.
在这种情况下是可以的,但是不需要从垃圾回收器中输出一个短小的日志信息,也不需要输出大量日志.
2.1.6 完全限制方式导包
导包时,使用全包格式,例如:
不要这样导包:
import android.support.v7.widget.*;
而是要这样:
import android.support.v7.widget.RecyclerView;
2.1.7 去掉无用的包引入
通常从一个类中移除代码意味着一些引入的包夜不在需要了。
这种情况下,不需要导入的包应该和代码一起移除掉。
2.2 Java样式规则
2.2.1 属性定义和命名
所有的字段都应该在文件顶部定义,遵循如下规则:
- private, non-static 字段不应该用“m”做前缀,像如下的例子是正确的:
userSignedIn, userNameText, acceptButton
而不是这样:
mUserSignedIn, mUserNameText, mAcceptButton - 所有其他的字段都用小写字母开头:
int numOfChildren; String username;
- static final 字段(静态的)全部大写
private static final int PAGE_COUNT = 0;
- 类属性命名没有显示任何信息的不应该适用,比如:
int e; //number of elements in the list
为什么一开始不给属性一个有意义的名字,而给一段comment呢?
int numberOfElements;
如上这样不就很好了嘛
2.2.1.2 View属性名称
当为一个界面控件定义一个类属性时,view应该作为这个属性的后缀,比如:
View
|
Name
|
---|---|
TextView | usernameView |
Button | acceptLoginView |
ImageView | profileAvatarView |
RelativeLayout | profileLayout |
我们这样命名view以便我们可以知道这个类属性和什么资源关联.
比如, 给定像这样的类属性命名:
usernameView,userAvatarView或者userProfieLayout
这些命名很清晰的表明其对应的资源
以前,控件属性的命名通常用控件类型来结尾(比如acceptLoginButton),但是控件经常改变而且很容易忘记到java类中去更新变量的名称.
2.2.2 命名属性时不要包含容器类型
根据上一条的规则,同样我们应该避免在创建集合变量时使用集合类型来命名.比如,假如我们有一个使用arrayList包含userIds的列表:
使用:
List userIds = new ArrayList<>();
不要使用:
List userIdList = new ArrayList<>();
当集合容器改变类型后,如上的命名可能会经常忘记去更改 - 就像控件命名那样,所以没有必要包含容器类型.
容器变量正确的命名应该包含足够的变量信息.
2.2.3 避免相似的命名
使用相似的名称来命名变量,方法或者类时,会使其他开发者在阅读您的代码时迷惑.比如:
hasUserSelectedSingleProfilePreviously
hasUserSelectedSignedProfilePreviously
很难第一眼就可以区分出上面两个变量是干什么的.使用更清晰的方式命名变量可以使得其他开发者更容易通过变量名理解您的代码.
2.2.4 数字连续命名
当Android Studio自动为我们产生代码,这样很容易给我们留下问题,特别是当它产生糟糕的命名时.比如,这样就很不好:
public void doSomething(String s1, String s2, String s3)
不阅读代码的话很难理解如上这个方法中的参数是做什么的.
public void doSomething(String userName, String userEmail, String userId)
这样就容易理解得多了.我们可以根据这个更容易理解的方法参数去阅读代码了
2.2.5 可读的命名
在对属性,方法和类命名时,应该遵循如下规则:
- 可 读:有效的命名意味着我们可以立刻通过名称就理解,减少译解名称的认知负担.
- 可拼写:在您尝试对命名很糟糕的名称发音时,可拼写的命名可以避免笨拙的对话.
- 可搜索:没有比在一个类中搜索一个方法或者变量时发现名称拼写错误或者命名很糟糕.当我们想找到一个关于查找用户的方法时,直接搜索字符串 ‘search’ 就应该保护这个方法的结果.
- 不要使用匈牙利标记法:匈牙利标记法和上面三条冲突,不要使用.
2.2.6 将缩写词(那些将所有字母都大写的词)作为单词使用
在任意类名,变量名中使用的缩写词都应该作为单词使用.比如:
Do
|
Don’t
|
---|---|
setUserId | setUserID |
String uri | String URI |
int id | int ID |
parseHtml | parseHTML |
generateXmlFile | generateXMLFile |
2.2.7 避免对齐变量声明
任何变量的声明都不应该使用特殊的对齐格式,比如:
这样就很好:
private int userId = 8;
private int count = 0;
private String username = "hitherejoe";
不要这样做:
private String username = "hitherejoe";
private int userId = 8;
private int count = 0;
这样产生了很多空白让代码难于阅读.
2.2.8 使用空白符进行缩进
对于语句块,应该使用4个空白符缩进:
if (userSignedIn) {
count = 1;
}
在自动换行时,应该使用8个空白符缩进:
String userAboutText =
"This is some text about the user and it is pretty long, can you see!"
2.2.9 If-Statement语句
2.2.9.1 使用标准的大括号样式
大括号应该与其前面的代码放置在同一行。例如,避免下面这样的代码出现:
class SomeClass
{
private void someFunction()
{
if (isSomething)
{
}
else if (!isSomethingElse)
{
}
else
{
}
}
}
取而代之应该这样:
class SomeClass {
private void someFunction() {
if (isSomething) {
} else if (!isSomethingElse) {
} else {
}
}
}
这不仅是没有必要增加额外的一行,而它还会让读取代码变得更加容易
2.2.9.2 Inline if-clauses 内联if从句
有时使用单行if语句是有意义的。例如:
if (user == null) return false;
然而,它仅适用于简单的操作。像下面这样的代码会更适合使用大括号:
if (user == null) throw new IllegalArgumentExeption("Oops, user object is required.");
2.2.9.3 Nested if-conditions 嵌套的if条件语句
在可能的情况下,if语句的条件应该合并,避免过于复杂的嵌套,例如:
推荐:
if (userSignedIn && userId != null) {
}
避免这样:
if (userSignedIn) {
if (userId != null) {
}
}
这会使得代码更加容易阅读,并且从嵌套子句中移除不必要的额外的行数。
2.2.9.4 Ternary Operators 三元运算
在适当的情况下,三元运算符能够用来简化操作。
例如,下面的代码很容易阅读:
userStatusImage = signedIn ? R.drawable.ic_tick : R.drawable.ic_cross;
它比下面的代码占用更少的行数:
if (signedIn) {
userStatusImage = R.drawable.ic_tick;
} else {
userStatusImage = R.drawable.ic_cross;
}
注意:在有些情况下三元运算符不应该使用。如果if语句的逻辑过于复杂或者包含了大量的字符,这时应该使用标准的大括号样式。
2.2.10 Annotations 注解
2.2.10.1 Annotation practices 注解用法
从《Android code style guidelines》中获悉:
@Override: @Override注解必须使用在当重写方法时候的声明或者实现父类时。例如,如果你使用 @inheritdocs文档标签,并且派生一个类(而不是一个接口),对于重写的父类方法你也必须使用 @Override注解。
@SuppressWarnings: @SuppressWarnings注解应该仅使用在不可能清除警告的情况下,如果一个警告通过了“不可能消除”测试, @SuppressWarnings注解必须使用,以便在代码中确保所有的警告反映实际问题。
有关注解指引的更多信息可以在这里找到。
注解应该使用在可能需要的地方。例如,可使用 @Nullable注解已免出现属性为空的情况。例如:
@Nullable TextView userNameText;
private void getName(@Nullable String name) { }
2.2.10.2 Annotation style 注解样式
应用在方法或者类的注解应该总是在声明中定义,并且每一项只能一行:
@Annotation
@AnotherAnnotation
public class SomeClass {
@SomeAnotation
public String getMeAString() {
}
}
当在属性上使用注解时,应确保注解与属性在同一行,例如:
@Bind(R.id.layout_coordinator) CoordinatorLayout coordinatorLayout;
@Inject MainPresenter mainPresenter;
它会使得声明阅读起来更加容易。例如,在声明 '@Inject SomeComponent mSomeName' 可读为'inject this component with this name'(利用这个名字注入组件)。
2.2.11 Limit variable scope 限制变量的作用域
局部变量的作用域应保持在最低限度(《Effective Java》第29条建议)。这样做的话,将会提高代码的可读性和可维护性,同时减少出错的可能性。所有要使用的每一个变量都应该在语句块中声明。
局部变量应当在其第一次使用处声明。几乎每一个局部变量的声明都应该包含一个初始化。如果你还没有足够的信息来合理地初始化变量,你应该推迟声明直到你需要的时候。-摘取自《Android code style guidelines》
2.2.12 Unused elements 无用的元素
所有无用的属性、导入、方法和类应该从代码中移除,除非有特殊的用途保留这些。
2.2.13 Order Import Statements 导入声明
因为我们使用的是Android Studio,因此导入都是自动的按序导入。然而,在一些情况下并非如此,因此应按照下面的顺序导入:
1.Android imports
2.Imports from third parties
3.java and javax imports
4.Imports from the current Project
注意:1.每个组的导入应按照字母的顺序排列,大写字母排在小写字母之前(Z排在a之前)
2.在各个组之间应该空一行(android, com, JUnit, net, org, java, javax)
2.2.14 Logging 日志
日志通常在开发过程中用于记录有用的错误消息或者其他可能有用的信息。
Log
|
Reason
|
---|---|
Log.v(String tag, String message) | verbose |
Log.d(String tag, String message) | debug |
Log.i(String tag, String message) | information |
Log.w(String tag, String message) | warning |
Log.e(String tag, String message) | error |
我们做日志记录时可以设置一个标示,这个标示是一个静态的final属性,放置在类的顶部,例如:
private static final String TAG = MyActivity.class.getName();
所有的调试日志不应该出现在发布的版本中,另一方面,信息、警告和错误日志只有在需要的时候保持开启。
if (BuildConfig.DEBUG) {
Log.d(TAG, "Here's a log message");
}
注意:Timber是使用起来比较好的日志方式,当我们需要保持一个TAG的引用时,它会为我们处理日志的标示。(看不懂这句话)
2.2.15 Field Ordering 属性排序
在类文件顶部声明的任何属性都应该按下列的排序规则进行排序:
1.Enums 枚举类型
2.Constants 常量
3.Dagger Injected fields Dagger注入的属性
4.Butterknife View Bindings Butterknife绑定的view
5.private global variables 私有变量
6.public global variables 公共变量
例如:
public static enum {
ENUM_ONE, ENUM_TWO
}
public static final String KEY_NAME = "KEY_NAME";
public static final int COUNT_USER = 0;
@Inject SomeAdapter someAdapter;
@BindView(R.id.text_name) TextView nameText;
@BindView(R.id.image_photo) ImageView photoImage;
private int userCount;
private String errorMessage;
public int someCount;
public String someString;
使用上述的排序规则有助于保持字段声明的分组,从而增加双方的位置和字段的可读性
2.2.16 Class member ordering 类成员排序
为了提高代码的可读性,组织类成员在一个合乎逻辑的方式中是非常的重要,请按下列的排序方式去实现:
1.Constants 常量
2.Fields 属性
3.Constructors 构造方法
4.Override methods and callbacks (public or private) 继承的方法和回调(公共或者私有)
5.Public methods 公共方法
6.Private methods 私有方法
7.Inner classes or interfaces 内部类或者接口
例如:
public class MainActivity extends Activity {
private int count;
public static newInstance() { }
@Override
public void onCreate() { }
public setUsername() { }
private void setupUsername() { }
static class AnInnerClass { }
interface SomeInterface { }
}
在Android框架中任何生命周期的方法应该在其相应的生命周期中排序,例如:
public class MainActivity extends Activity {
// Field and constructors
@Override
public void onCreate() { }
@Override
public void onStart() { }
@Override
public void onResume() { }
@Override
public void onPause() { }
@Override
public void onStop() { }
@Override
public void onRestart() { }
@Override
public void onDestroy() { }
// public methods, private methods, inner classes and interfaces
}
2.2.17 Method parameter ordering 方法的参数排序
当定义方法时,参数应该按照下列的规则排序:
public Post loadPost(Context context, int postId);
public void loadPost(Context context, int postId, Callback callback);
Context上下文参数应放在第一位,并且Callback回调参数放置在最后
2.2.18 String constants, naming, and values 字符串常量、命名和值
当使用字符串常量时,其应该修饰为静态final并且遵循下列规则:
2.2.19 Enums 枚举
枚举的使用仅仅在实际需要用到时。如果另外一种方法可行,此时应该选择更好的方式去实现它,例如:
相对于下面这样:
public enum SomeEnum {
ONE, TWO, THREE
}
更推荐这样做:
private static final int VALUE_ONE = 1;
private static final int VALUE_TWO = 2;
private static final int VALUE_THREE = 3;
2.2.20 Arguments in fragments and activities 在fragment和activity中的参数
当我们使用Intent或者Bundle传递数据时,值的键必须使用下面定义的约定:
Activity
传递数据到一个activity必须使用一个KEY的引用,像下面这样定义:
private static final String KEY_NAME = "com.package.name.activity.KEY_NAME";
Fragment
传递数据到一个fragment必须使用一个EXTRA的引用,像下面这样定义:
private static final String EXTRA_NAME = "EXTRA_NAME";
当创建fragment或者activity的新实例涉及到传递数据时,我们应该提供一个静态的方法来获取新的实例,传递的数据作为参数。例如:
Activity
public static Intent getStartIntent(Context context, Post post) {
Intent intent = new Intent(context, CurrentActivity.class);
intent.putParcelableExtra(EXTRA_POST, post);
return intent;
}
Fragment
public static PostFragment newInstance(Post post) {
PostFragment fragment = new PostFragment();
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_POST, post);
fragment.setArguments(args)
return fragment;
}
2.2.21 Line Length Limit 行长度限制
代码的行数字符长度最好不要超过100个字符,这样代码的可读性会更高。有时为了实现上述要求,我们需要做的:
1.提取数据到一个局部变量
2.提取代码逻辑到外部的方法
3.将一段较长的代码换行显示
注意:对于代码的注释和导入声明,超过100个字符的限制是可以的。
2.2.21.1 Line-wrapping techniques 换行技巧
当涉及到换行时,有一些情况我们应该保持与格式化代码的一致性。
运算符换行
当我们需要在一个运算公式换行时,需要在运算符前面换行:
int count = countOne + countTwo - countThree + countFour * countFive - countSix
+ countOnANewLineBecauseItsTooLong;
如果需要,你可以直接在“=”后换行:
int count =
countOne + countTwo - countThree + countFour * countFive + countSix;
方法链
当涉及到链接方法,每个方法的调用都应该另起一行:
不要下面这样:
Picasso.with(context).load("someUrl").into(imageView);
取而代之应这样:
Picasso.with(context)
.load("someUrl")
.into(imageView);
长参数
对于一个含有长参数的方法,我们在适当的情况下应该换行。例如当声明一个方法时我们应该在最后一个参数的逗号处换行:
private void someMethod(Context context, String someLongStringName, String text,
long thisIsALong, String anotherString) {
}
当调用这个方法时,我们应该在每个参数的逗号后面换行:
someMethod(context,
"thisIsSomeLongTextItsQuiteLongIsntIt",
"someText",
01223892365463456,
"thisIsSomeLongTextItsQuiteLongIsntIt");
2.2.22 Method spacing(方法间间距)
在同一个类中,方法与方法只需要留有一行的间隙,如下:
public String getUserName() {
// Code
}
public void setUserName(String name) {
// Code
}
public boolean isUserSignedIn() {
// Code
}
2.2.23 Comments(注释)
2.2.23.1 Inline comments(行内注释 or 内敛注释)
必要的时候,内敛注释是为了解释给读者特定代码段的意思。在代码比较复杂的情况下才添加注释,大多数时候,代码应该以浅显易懂的方式写出来,而不用添加注释。
(注意:代码注释不是非得有,但最好有,注释尽量保持在100字符以内)
2.2.23.2 JavaDoc Style Comments(java文档的注释风格)
方法的名字应该起的和该方法的功能相对应,有时可以提供JavaDoc风格的注释。方法名起的好会帮助读者更好的理解方法的功能,同时也会让读者明白传入方法中参数的作用。
/**
* Authenticates the user against the API given a User id.
* If successful, this returns a success result
*
* @param userId The user id of the user that is to be authenticated.
*/
2.2.23.3 Class comments(类注释)
在创建类注释时,它们应该是有意义的,有描述性的,必要的时候使用超链接。如下:
/**
* RecyclerView adapter to display a list of {@link Post}.
* Currently used with {@link PostRecycler} to show the list of Post items.
*/
不要写初创作者信息,因为以后会有很多人在这个类上修修补补,写上作者信息是没有任何意义的。
/**
* Created By Joe 18/06/2016
*/
2.2.24 Sectioning code(分段代码)
2.2.24.1 Java code(java代码)
如果对代码做了“分段”,应该使用下面的方法完成,如下:
public void method() { }
public void someOtherMethod() { }
/********* Mvp Method Implementations ********/
public void anotherMethod() { }
/********* Helper Methods ********/
public void someMethod() { }
不能像下面这样:
public void method() { }
public void someOtherMethod() { }
// Mvp Method Implementations
public void anotherMethod() { }
这样会更容易定位类中方法。
2.2.24.2 Strings file(字符串文件)
字符串资源文件string.xml中分段注释如下:
// User Profile Activity
<string name="button_save">Save</string>
<string name="button_cancel">Cancel</string>
// Settings Activity
<string name="message_instructions">...</string>
这样写不仅可以让字符串文件看起来整洁,还能在需要更改它们时更容易找到。
2.2.24.3 RxJava chaining(RxJava链接)
当进行异步操作时,每一步操作都应该在遇到“.”号之前另起一行,如下:
return dataManager.getPost()
.concatMap(new Func1<Post, Observable<? extends Post>>() {
@Override
public Observable<? extends Post> call(Post post) {
return mRetrofitService.getPost(post.id);
}
})
.retry(new Func2<Integer, Throwable, Boolean>() {
@Override
public Boolean call(Integer numRetries, Throwable throwable) {
return throwable instanceof RetrofitError;
}
});
这样会使读者更容易理解接下来的异步操作。
2.2.25 Butterknife(Butterknife)
2.2.25.1 Event listeners(事件监听者)
如有可能,尽量使用ButterKnife绑定监听。举个栗子,可以用ButterKnife替换传统的点击事件:
mSubmitButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Some code here...
}
};
换成如下:
@OnClick(R.id.button_submit)
public void onSubmitButtonClick() { }
2.3 XML Style Rules(XML文件中样式规则)
2.3.1 Use self=-closing tags(使用单标记)
在xml布局中,如果一个viwe没有任何子view,那么就应该使用单标记。
用这个:
<ImageView
android:id="@+id/image_user"
android:layout_width="90dp"
android:layout_height="90dp" />
不用这个:
<ImageView
android:id="@+id/image_user"
android:layout_width="90dp"
android:layout_height="90dp">
</ImageView>
2.3.2 Resource naming(资源命名)
所有的资源命名规则都应该是小写和下划线的组合,如下:
2.3.2.1 ID naming(id命名)
所有的id命名规则都应该用元素作为前缀。
Element | Prefix |
ImageView | image_ |
Fragment | fragment_ |
RelativeLayout | layout_ |
Button | button_ |
TextView | text_ |
View | view_ |
例如:
<TextView
android:id="@+id/text_username"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
注意:如果一个布局中一种类型的view只有一种,比方toolbar,那么可以直接起名叫toolbar
2.3.2.2 Strings(字符串)
所有的字符串名字应该以该应用的当前功能页面作为前缀,如下:
Screen | String | Resource Name |
Registration Fragment | “Register now” | registration_register_now |
Sign Up Activity | “Cancel” | sign_up_cancel |
Rate App Dialog | “No thanks” | rate_app_no_thanks |
如果没法像上面一样命名,咱们可以用下面的方法:
Prefix | Description |
error_ | Used for error messages |
title_ | Used for dialog titles |
action_ | Used for option menu actions |
msg_ | Used for generic message such as in a dialog |
label_ | Used for activity labels |
需要注意以下两点:
1、同一个的字符串资源不能在多个文件中共享使用。如果其中的一个页面字符串发生改变也会造成另一个页面的改变从而产生问题。每个页面使用单独的字符串资源会给将来省去很多麻烦。
2、字符串资源必须放在字符串资源文件中,不能写在布局或者类中。
2.3.2.3 Styles and themes(风格 & 主题)
当定义风格和主题时,每个单词应该大写开头。如下:
AppTheme.DarkBackground.NoActionBar
AppTheme.LightBackground.TransparentStatusBar
ProfileButtonStyle
TitleTextStyle
2.3.3 Attributes ordering(属性排序)
定义属性不能只为了看起来整洁,同时能够在布局中快速找到属性位置。以下是基本规则:
1、viwe的id
2、风格
3、布局的宽高
4、别的布局属性,按照字母顺序排序
5、剩下的属性,按照字母顺序排序
如下:
<Button
android:id="@id/button_accept"
style="@style/ButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:padding="16dp"
android:text="@string/button_skip_sign_in"
android:textColor="@color/bluish_gray" />
注意:在Android studio中快速格式化快捷键是:cmd + shift + L
这样做,当布局文件发生变化时,可以通过xml属性快速定位。
2.4 Tests style rules(测试风格规则)
2.4.1 Unit tests(单元测试)
所有测试类起名字都应该和他们被测试的类相对应,并且以Test作为后缀,如下:
Class | Test Class |
DataManager | DataManagerTest |
UserProfilePresenter | UserProfilePresenterTest |
PreferencesHelper | PreferencesHelperTest |
所有的测试方法应该用@Test进行注释,测试方法应该用下面的模板:
@Test
public void methodNamePreconditionExpectedResult() { }
举个栗子,如果我们想检测一个使用无效电子邮件地址名叫signUp()方法时,测试方法应该使用如下的:
@Test
public void signUpWithInvalidEmailFails() { }
测试应该将重点放在测试方法赋予的功能名称上面,如果在你的测试方法中还有别的情况需要考虑,这些额外的需要测试的情况应该分到它专门的测试方法中。
如果一个类中包含许多不同的方法,测试应该在多个测试类中进行拆分-这样有助于测试更易于维护和定位。例如,一个数据库工具类有时候会分解成如下几个测试类:
DatabaseHelperUserTest
DatabaseHelperPostsTest
DatabaseHelperDraftsTest
2.4.2 Espresso tests(功能测试框架Espresso)
每个Espresso测试类一般都对应一个Activity,所以命名时应该和对应的Activity相一致,其次是测试,如下:
Class | Test Class |
MainActivity | MainActivityTest |
ProfileActivity | ProfileActivityTest |
DraftsActivity | DraftsActivityTest |
当使用Espresso API的时候,方法应该换行从而可以让声明更加宜读,举例如下:
onView(withId(R.id.text_title))
.perform(scrollTo())
.check(matches(isDisplayed()))
这种风格的链接调用不仅可以让我们每行不超过100个字符,同时也可以让Espresso测试中的链接更加易读。
3. Gradle Style(Gradle风格)
3.1 Dependencies(依赖)
3.1.1 Versioning(版本控制)
如果一个版本号在多个依赖中多被使用,那么应该在依赖的范围内定义成一个变量,如下:
final SUPPORT_LIBRARY_VERSION = '23.4.0'
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:percent:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:customtabs:$SUPPORT_LIBRARY_VERSION"
将来如果需要更新依赖,那么只需要更新版本号的变量就可以很轻松的控制所有依赖的版本号。
3.1.2 Grouping(分组)
依赖应该以包名来分组,各个组之间应该有一定的间隙,如下:
compile "com.android.support:percent:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:customtabs:$SUPPORT_LIBRARY_VERSION"
compile 'io.reactivex:rxandroid:1.2.0'
compile 'io.reactivex:rxjava:1.1.5'
compile 'com.jakewharton:butterknife:7.0.1'
compile 'com.jakewharton.timber:timber:4.1.2'
compile 'com.github.bumptech.glide:glide:3.7.0'
Compile、testCompile、androidTestCompile依赖同样应该分组到相对应的组别中,如下:
// App Dependencies
compile "com.android.support:support-v4:$SUPPORT_LIBRARY_VERSION"
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
// Instrumentation test dependencies
androidTestCompile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
// Unit tests dependencies
testCompile 'org.robolectric:robolectric:3.0'
这两种方法都可以很容易的找到特定的依赖关系,需要时,它保证依赖的声明既干净又整洁。
3.1.3 Independent Dependencies(独立的依赖关系)
依赖只能应用在应用或者目的测试中,确保使用compile,testCompile,androidTestCompile来编译它们。例如,robolectric依赖只能被用来做单元测试,它应该如下:
testCompile 'org.robolectric:robolectric:3.0'