译自:project_and_code_guidelines
1.项目规范
1.1工程结构
Android项目应当遵循Android Gradle plugin user guide所定义的Android项目结构规范!一个很好的项目示例:ribot Boilerplate。
1.2文件命名
1.2.1 类文件
class文件命名规则遵循驼峰式命名:UpperCamelCase。
继承自Android组件的class文件需要以Android组件的名字为后缀,例如:
SignInActivity
,SignInFragment
,ImageUploaderService
,ChangePasswordDialog
。
1.2.2资源文件
- 资源文件命名规范遵循:小写字母加下划线。
1.2.2.1 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 |
图标的命名规范:
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 |
表明选择状态的命名规范:
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 |
1.2.2.2 layout文件命名规范
布局文件名字应该与相应的Android组件的名字相匹配,并且将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 |
- 当我们给ListView
的每一项创建布局的时候,布局应该以item_
开始。
- 当上述规则无法满足的时候,比如该布局文件是另一个布局文件的一部分,这时候就应该以partial_
开头。
1.2.2.3 Menu File
与layout文件相同,我们命名menu文件也应该与相应地组件名字进行匹配,假如我们要为UserActivity创建一个menu file,它应该命名为:activity_user.xml
。
这个文件已经在menu的文件目录下,所以不用再包含关键字menu
2.代码规范
2.1 Java语言规范
2.1.1 必须处理异常
绝不能这样做:
void setServerPort(String value) {
try {
serverPort = Integer.parseInt(value);
} catch (NumberFormatException e) { }
}
绝对不能因为你认为你的代码不会遇到这个错误或者,这个错误不重要就不去处理这个异常,必须根据不同的情况去处理各种异常。参考…
2.1.2 不要抛出普遍的异常
当代码会抛出各种各样的异常的时候,应该把每一种异常都抛出来,而不是只抛出这些异常的父类:
try {
someComplicatedIOFunction(); // may throw IOException
someComplicatedParsingFunction(); // may throw ParsingException
someComplicatedSecurityFunction(); // may throw SecurityException
// phew, made it all the way
} catch (Exception e) { // I'll just catch all exceptions
handleError(); // with one generic handler!
}
2.1.3 不要使用finalizer
我们不要使用finalizer,我们无法确定它什么时候被调用或者是否会被调用。参考…
2.1.4 不要导入一个包下的所有类
错误: import foo.*;
正确: import foo.Bar;
2.2 Java风格规范
2.2.1 成员变量的命名和定义
- private non-static 类型变量以
m
开头; - private static 类型的变量以
s
开头; - 其他类型的变量以小写字母开头
- static final 类型的变量必须全部使用大写字母;
例如:
public class MyClass {
public static final int SOME_CONSTANT = 42;
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
2.2.2 将首字母缩略词视为一个单词
不要将一个缩略词全部大写:
Good | Bad |
---|---|
XmlHttpRequest | XMLHTTPRequest |
getCustomerId | getCustomerID |
String url | String URL |
long id | long ID |
2.2.3 使用空格进行代码缩进
- 代码块的缩进使用4个空格:
if (x == 1) {
x++;
}
- 折行的时候使用8个空格:
Instrument i =
someLongExpression(that, wouldNotFit, on, one, line);
2.2.4 使用标准的结构
即:规范的使用大括号:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
不使用大括号的时候,代码体应该简短至一行:
if(condition) body();
不应该这样写:
if(condition)
body();
2.2.5 注解(Annotations)
2.2.5.1使用注解
根据Android代码风格规范,一些标准的注解使用如下:
* @override
: 在重写父类或者接口中的方法时,必须使用这个注解。
* @SuppressWarnings
: 当我们不可能去消除一个警告的时候,使用这个注解!这样可以确保所有的警告信息可以真实的反应我们代码中的实际问题!
参考…。
2.2.5.2 注解的风格
Classes, Methods and Constructors
当我们在一个类、方法或者是构造方法中使用注解的时候,把注解放在注释的后面,并且每一个注解占据一行。
/* This is the documentation block about the class */
@AnnotationA
@AnnotationB
public class MyAnnotatedClass { }
Fields
对成员变量使用注解必须全部放在一行,除非到达行末。
@Nullable @Mock DataManager mDataManager;
2.2.6 限制变量的作用域
- 局部变量应该保持最小的作用域,这样可以提高代码的易读性和可维护性,并且减少代码出错的可能性。
- 局部变量应该在它第一次使用的地方声明,几乎所有的局部变量都应该初始化,如果你并没有明确的信息去初始化一个局部变量,那么这个局部变量应该推迟声明。
2.2.7 对import声明进行排序
如果你使用Android Studio,那么就不用担心这个问题,Android Studio会自动帮你解决这个问题。如果不是,请按照以下顺序进行声明:
1. Android imports(Android相关的包)
2. Imports from third parties(第三方的包)
3. java and javax
4. Project import (项目内的包)
此外,为了匹配IDE的设置,imports还应该遵循:
* 按照以上顺序声明进行分组(例如:android, com, junit, net, org, java, javax),每组中间大写字母应该在小写字母的前面。
* 每组中间应该有空格分隔开来。
2.2.8 Logging 规范
使用Log
类去打印有用的信息:
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)
一般来说,我们使用类的名字去定义一个static final
类型的成员变量作为tag, 例如:
public class MyClass {
private static final String TAG = MyClass.class.getSimpleName();
public myMethod() {
Log.e(TAG, "My error message");
}
}
所有的Log信息必须在release版本中去除,因为这些可能会丢失一些关键的信息!
2.2.9 类成员的排序
对类的成员进行排序将会提高代码的可读性,我们建议使用下列顺序:
- Constants(常量)
- Fields(成员变量)
- Constructors (构造方法)
- Override methods and callbacks (public or private) (重写的方法或者回调)
- Public methods(公有方法)
- Private methods (私有方法)
- Inner classes or interfaces(内部类或者接口)
例如:
public class MainActivity extends Activity {
private String mTitle;
private TextView mTextViewTitle;
public void setTitle(String title) {
mTitle = title;
}
@Override
public void onCreate() {
...
}
private void setUpView() {
...
}
static class AnInnerClass {
}
}
如果一个类继承自Android的组件,这个类中重写的Android组件的生命周期方法要按照组件的生命周期来写:
public class MainActivity extends Activity {
//Order matches Activity lifecycle
@Override
public void onCreate() {}
@Override
public void onResume() {}
@Override
public void onPause() {}
@Override
public void onDestroy() {}
}
2.2.10 方法参数的顺序
Android开发中经常会遇到传递Context参数的情况,这时候Context参数必须作为第一参数传递。
相反的是Callback必须作为方法的最后一个参数:
// Context always goes first
public User loadUser(Context context, int userId);
// Callbacks always go last
public void loadUserAsync(Context context, int userId, UserCallback callback);
2.2.12 String类型的常量,命名和值
Andorid SDK中会有很多这样的变量:SharedPreferences
,Bundle
,或者Intent
使用键值对,所以我们会创建大量的String常量。
当我们使用这个组件的时候,我们必须定义static final
类型的key值,并且它们要有一个恰当的前缀用以提示:
Element | Field Name Prefix |
---|---|
SharedPreferences | PREF_ |
Bundle | BUNDLE_ |
Fragment Arguments | ARGUMENT_ |
Intent Extra | EXTRA_ |
Intent Action | ACTION_ |
例如:
// Note the value of the field is the same as the name to avoid duplication issues
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// Intent-related items use full package name as value
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
2.2.13 Fragments and Activities中的参数
当数据通过Intent
或者是Bundle
在Activity
或者Fragment
中间传递的时候,它们所用到的key值都必须按照上一节的规则。
当Activity
或者是Fragment
想要获取这些参数的时候,它应该提供一个public static
的方法以便于去获取相应的Intent
。
Activity中这个方法通常写作getStartIntent()
:
public static Intent getStartIntent(Context context, User user) {
Intent intent = new Intent(context, ThisActivity.class);
intent.putParcelableExtra(EXTRA_USER, user);
return intent;
}
Fragment之中则写为newInstance
:
public static UserFragment newInstance(User user) {
UserFragment fragment = new UserFragment;
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
fragment.setArguments(args)
return fragment;
}
注意 1: 这些方法必须放在类的顶部onCreate()
.
注意 2: 如果我们有这样的方法,那么这些key值都必须使用private
进行声明,它们没有必要暴露给额外的类。
2.2.14 限制每一行的长度
代码每一行不应该超过100个字符,如果每一行的长度过长,使用以下两种方式去减少行的长度:
* 提取一些局部变量和方法。
* 使用折行将一行换成多行。
2.2.14.1 折行的策略
没有一种特定的折行方式,但是有一些折行的规则如下:
操作符折行
操作符前折行,例如:
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
等号操作符处后面折行
当操作符是=
的时候,应该在=
后面折行:
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
Method chain case
当遇到链式的方法调用的时候,应当在.
之前折行:
Picasso.with(context)
.load("http://ribot.co.uk/images/sexyjoe.jpg")
.into(imageView);
方法参数过多
方法参数过多应该以每个参数后面的逗号后面折行
loadPicture(context,
"http://ribot.co.uk/images/sexyjoe.jpg",
mImageViewProfilePicture,
clickListener,
"Title of the picture");
2.2.15 Rxjava链式风格
Rx chains of operators require line-wrapping. Every operator must go in a new line and the line should be broken before the .
public Observable<Location> syncLocations() {
return mDatabaseHelper.getAllLocations()
.concatMap(new Func1<Location, Observable<?extends Location>>() {
@Override
public Observable<? extends Location> call(Location location) {
return mRetrofitService.getLocation(location.id);
}
})
.retry(new Func2<Integer, Throwable, Boolean>() {
@Override
public Boolean call(Integer numRetries, Throwable throwable) {
return throwable instanceof RetrofitError;
}
});
}
2.3 XML 风格规范
2.3.1 Use self closing tags
当一个元素不包含内容的时候,必须自闭
This is good:
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
This is bad :
<!-- Don\'t do this! -->
<TextView
android:id="@+id/text_view_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</TextView>
2.3.2 资源文件命名
资源id和名称必须全部使用小写字母和下划线。
2.3.2.1 资源ID 命名
id应该以对应的组件名字为前缀,加下划线命名:
Element | Prefix |
---|---|
TextView | text_ |
ImageView | image_ |
Button | button_ |
Menu | menu_ |
Image view example:
<ImageView
android:id="@+id/image_profile"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Menu example:
<menu>
<item
android:id="@+id/menu_done"
android:title="Done" />
</menu>
2.3.2.2 Strings
String文件命名应该以它所属于的模块内容名字为前缀,例如:registration_email_hint
or registration_name_hint
.若不属于任何模块,则遵循以下规则:
Prefix | Description |
---|---|
error_ | An error message |
msg_ | A regular information message |
title_ | A title, i.e. a dialog title |
action_ | An action such as “Save” or “Create” |
2.3.2.3 Styles and Themes
样式文件名称都使用驼峰式命名。
2.3.3 Attributes ordering
组件的属性排序按照以下顺序
- View Id
- Style
- Layout width and layout height
- Other layout attributes, sorted alphabetically
- Remaining attributes, sorted alphabetically