1.AS规范
- 尽量使用最新的稳定的IDE进行开发;
- 编码格式统一为 UTF-8;
- 删除多余的 import,减少警告出现,可利用 AS 的 Optimize Imports(Settings -> Keymap -> Optimize Imports)快捷键;
2.命名规范
代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
2.1 包
2.1.1 包名
- 包名必须是全部小写,连续的单词只是简单地连接起来,不使用下划线,采用反域名命名规则。
- 一级包名为顶级域名,通常为 com、 edu 等
- 二级包名为公司名,如 denglin
- 三级包名为应用名,如 rubbish
2.1.2 包名划分
- 推荐采用PBF(按照功能分包Package By Feature)不建议使用PBL(按层分包 Package By Layer)
- 按照功能分包具体可以这样做:
com
└── domain
└── app
├── App.java 定义 Application 类
├── Config.java 定义配置数据(常量)
├── Activity
│ ├── HomeActivity
│ ├── LoginActivity
│ ├── AboutActivity
├── base 基础组件
├── custom_view 自定义视图
├── data 数据处理
│ ├── DataManager.java 数据管理器
| ├── Aes.java Aes数据加密解密
│ ├── local 来源于本地的数据,比如 SP,Database,File
│ ├── model 定义 model(数据结构以及 getter/setter、compareTo、equals 等等,不含复杂操作)
│ └── remote 来源于远端的数据
├── feature 功能
│ ├── feature0 功能 0(搜索垃圾分类)
│ │ ├── feature0Fragment.java
│ │ ├── xxAdapter.java
│ │ └── ... 其他 class
| ├── feature1 功能 1(获取垃圾搜索热点)
│ │ ├── feature1Fragment.java
│ │ ├── xxAdapter.java
│ │ └── ... 其他 class
| ├── feature2 功能 2(app启动时同步最新垃圾分类数据)
│ │ ├── feature1Fragment.java
│ │ ├── xxAdapter.java
│ │ └── ... 其他 class
│ └──....其他功能
├── injection 依赖注入
├── util 工具类(例如网络请求,数据上传)
└── widget 小部件
2.2 类
2.2.1 类名
- 使用驼峰命名 例如: LoginActivity
- 尽量不使用缩写
常见类命名格式
| 类 | 描述 | 例如 |
|---|---|---|
| Activity 类 | Activity 为后缀标识 | 主页面类 HomeActivity |
| Adapter 类 | Adapter 为后缀标识 | 新闻详情适配器 NewsDetailAdapter |
| 解析类 | Parser 为后缀标识 | 首页解析类 HomePosterParser |
| 工具方法类 | Utils 或 Manager 为后缀标识 | 打印工具类:PrinterUtils |
| 数据库类 | 以 DBHelper 后缀标识 | 新闻数据库:NewsDBHelper |
| Service 类 | 以 Service 为后缀标识 | 时间服务 TimeService |
| BroadcastReceiver 类 | 以 Receiver 为后缀标识 | 推送接收 JPushReceiver |
| ContentProvider 类 | 以 Provider 为后缀标识 | ShareProvider |
| 自定义的共享基础类 | 以 Base 开头 | BaseActivity, BaseFragment |
2.2.2 接口
- 使用驼峰命名 多以able或者ible结尾 例如:interface Runnable
- 如果项目采用MVP设计,所有 Modle 、 View 、 Presenter 的接口都是以 I 为前缀,不加后缀,其他的接口采用上述命名规则。
2.3 方法名
- 方法名以 lowerCamelCase 风格编写。
- 方法名通常是动词或动词短语。
常见方法命名
| 方法 | 说明 |
|---|---|
| initXX() | 初始化相关方法,例:初始化布局 initView() |
| isXX() checkXX() | 方法返回boolean类型 |
| getXXX() | 返回某个值的方法 |
| setXXX() | 设置某个属性值 |
| handleXXX() processXXX() | 对数据进行处理的方法 |
| displayXXX() showXX() | 显示提示框或者消息提示 |
| updateXX() | 更新数据 |
| saveXXX() | 保存数据 |
| clearXXX() | 清除数据 |
| removeXXX() | 移除数据或者视图等,如:removeView() |
| drawXXX() | 绘制数据或者效果相关等 |
2.4 常量名
- 常量名命名全部字母大写, 用下划线分割单词。 例如: CONSTANT_CAS
- 常量名都是一个静态 final 字段,但不是所有静态 final 字段都是常量。
static final int VERSION = 1;
static final String VERSION_NAME "hello world";
2.5 非常量字段名
非常量字段名以 lowerCamelCase 风格的基础上改造为如下风格: 基本结构为 scopeVariableNameType 。
2.5.1 scope (范围)
- 非公有,非静态字段命名为 m 开头。
- 静态字段命名为 s 开头。
- 其他字段以小写字母开头。
例如:
public class MyClass {
public int publicField;
private static MyClass sSingleton;
int mPackagePrivate;
private int mPrivate;
protected int mProtected;
}
使用 1 个字符前缀来表示作用范围,1 个字符的前缀必须小写,前缀后面是由表意性强的一个单词或多个单词组成的名字,而且每个单词的首写字母大写,其它字母小写,这样保证了对变量名能够进行正确的断句。
2.5.2 控件类型
为了避免控件和普通成员变量混淆以及更好地表达意思,所有用来表达控件的成员变量统一加上控件缩写作为前缀
UI控件缩写表
| 名称 | 缩写 |
|---|---|
| Button | btn |
| CheckBox | cb |
| EditText | et |
| FrameLayout | fl |
| GridView | gv |
| ImageButton | ib |
| ImageView | iv |
| LinearLayout | ll |
| ListView | lv |
| ProgressBar | pb |
| RadioButtion | rb |
| RecyclerView | rv |
| RelativeLayout | rl |
| ScrollView | sv |
| SeekBar | sb |
| Spinner | spn |
| TextView | tv |
| ToggleButton | tb |
| VideoView | vv |
| WebView | wv |
2.6 变量名
变量名中可能会出现量词,我们需要创建统一的量词,他们更容易理解,也更容易搜索。 例如: mFirstBook、 mPreBook 、 curBook 。
| 量词列表 | 量词后缀说明 |
|---|---|
| First | 一组变量中的第一个 |
| Last | 一组变量中的最后一个 |
| Next | 一组变量中的下一个 |
| Pre | 一组变量中的上一个 |
| Cur | 一组变量中的当前变量 |
3. 代码规范
3.1使用标准大括号样式
左大括号不单独占一行,与其前面的代码位于同一行:
class MyClass {
int func() {
if (something) {
// ...
} else if (somethingElse) {
// ...
} else {
// ...
}
}
}
需要在条件语句周围添加大括号。例外情况整个条件语句适合放在同一行,那么可以将其全部放在一行上。例如,我们接受一下样式:
if (condition) {
body();
}
也可以接受一下样式:
if (condition) body();
但不接受一下样式:
if (condition)
body();
3.2 类成员的顺序
推荐使用如下的排序顺序:
- 常量
- 字段
- 构造函数
- 重写函数和回调
- 公有函数
- 私有函数
- 内部类或接口 例如:
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private String mTitle;
private TextView mTextViewTitle;
@Override
public void onCreate() {
...
}
public void setTitle(String title) {
mTitle = title;
}
private void setUpView() {
...
}
static class AnInnerClass {
}
}
如果类继承于Android组件例如:Activity,那么把重写函数按照他们的生命周期进行排序是一个非常好的习惯。例如:
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() {}
}
3.3 函数参数的排序
在Android开发过程中, Context 在函数参数中是再常见不过的了,我们最好把 Context 作为函数的第一个参数。
相反,我们回调接口应该作为其最后一个参数。
例如:
// Context always goes first
public User loadUser(Context context, int userId);
// Callbacks always go last
public void loadUserAsync(Context context, int userId, UserCallback callback);
3.4 字符串常量的命名和值
Android SDK中的很多类都用到了键值对函数,比如 SharedPreferences 、 Bundle 、 Intent ,所以,即便是一个小应用,我们最终也不得不编写大量的字符串常量。
当我们用到这些类时,必须将他们的将定义为 static final 字段,并且遵循以下指示作为前缀。
| 类 | 字段名前缀 |
|---|---|
| SharedPreferences | PREF_ |
| Bundle | BUNDLE_ |
| Fragment Arguments | ARGUMENT_ |
| Intent Extra | EXTRA_ |
| Intent Action | ACTION_ |
说明:虽然 Fragment.getArguments() 得到的也是 Bundle ,但因为这是 Bundle 的常用用法,所以特意为此定义一个不同的前缀。
例如:
// 注意:字段的值与名称相同以避免重复问题
static final String PREF_EMAIL = "PREF_EMAIL";
static final String BUNDLE_AGE = "BUNDLE_AGE";
static final String ARGUMENT_USER_ID = "ARGUMENT_USER_ID";
// 与意图相关的项使用完整的包名作为值的前缀
static final String EXTRA_SURNAME = "com.myapp.extras.EXTRA_SURNAME";
static final String ACTION_OPEN_USER = "com.myapp.action.ACTION_OPEN_USER";
3.5 Activities 和 Fragments 的传参
当 Activity 或 Fragment 传递数据通过 Intent 或 Bundle 时,不同值的键须遵循上一条所提及到的。
当 Activity 或 Fragment 启动需要传递参数时,那么它需要提供一个 public static 的函数来帮助启动或创建它。
这方面,AS 已帮你写好了相关的 Live Templates,启动相关 Activity 的只需要在其内部输入 starter 即可生成它的启动器,如下所示:
public static void start(Context context, User user) {
Intent starter = new Intent(context, MainActivity.class);
starter.putParcelableExtra(EXTRA_USER, user);
context.startActivity(starter);
}
同理,启动相关 Fragment 在其内部输入 newInstance 即可,如下所示:
public static MainFragment newInstance(User user) {
Bundle args = new Bundle();
args.putParcelable(ARGUMENT_USER, user);
MainFragment fragment = new MainFragment();
fragment.setArguments(args);
return fragment;
}
4. 行长限制
4.1 换行策略
- 除赋值操作之外,我们把换行符放在操作符之前,例如:
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
- 赋值操作符的换行我们放在其后,例如:
int longName =
anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;
4.2 函数链的换行
当同一行中调用多个函数时,对每个函数的调用都应该在行的一行中,我们把换行符插入在 . 之前。
例如:
Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);
应该使用如下规则:
Picasso.with(context)
.load("https://blankj.com/images/avatar.jpg")
.into(ivAvatar);
4.3 多参数的换行
当一个方法有很多参数或者参数很长的时候,我们应该在每个 , 后面进行换行。 比如:
loadPicture(context, "https://blankj.com/images/avatar.jpg", ivAvatar, "Avatar of the user", clickListener);
应该使用如下规则:
loadPicture(context,
"https://blankj.com/images/avatar.jpg",
ivAvatar,
"Avatar of the user",
clickListener);
4.4 RxJava 链式换行
RxJava 的每个操作符都需要换新行,并且把换行符插入在 . 之前。
例如:
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;
}
});
}
5.资源文件规范
6.注释规范
6.1类注释
每个类完成后应该有作者姓名和联系方式的注解,对自己的代码负责。 例如:
/**
* author : Blankj
* time : 2019/07/13
* desc : xxxx 描述
* version: 1.0
*/
public class WelcomeActivity {
...
}
具体可以在AS在那个自己配置,进入 Settings -> Editor -> File and Code Templates -> Includes -> File Header,输入
/**
* author : ${USER}
* time : ${YEAR}/${MONTH}/${DAY}
* desc :
* version: 1.0
*/
这样可在每次新建类的时候自动加上该头注释。
6.2方法注释
每一个成员方法(包括自定义成员方法、覆盖方法、属性方法)的方法头都必须做方法头注释,在方法前一行输入 /** + 回车 或者设置 Fix doc comment(Settings -> Keymap -> Fix doc comment)快捷键,AS 便会帮你生成模板,我们只需要补全参数即可,如下所示。
/**
* bitmap 转 byteArr
*
* @param bitmap bitmap 对象
* @param format 格式
* @return 字节数组
*/
public static byte[] bitmap2Bytes(Bitmap bitmap, CompressFormat format) {
if (bitmap == null) return null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(format, 100, baos);
return baos.toByteArray();
}
6.3块注释
块注释与其周围的代码在同一缩进级别。它们可以是 / ... / 风格,也可以是 // ... 风格(// 后最好带一个空格)。对于多行的 / ... / 注释,后续行必须从 ***** 开始, 并且与前一行的 ***** 对齐。以下示例注释都是 OK 的。
/*
* This is
* okay.
*/
// And so
// is this.
/* Or you can
* even do this. */
6.4其他注释
AS 已帮你集成了一些注释模板,我们只需要直接使用即可,在代码中输入 todo、fixme 等这些注释模板,回车后便会出现如下注释。
// TODO: 17/3/14 需要实现,但目前还未实现的功能的说明
// FIXME: 17/3/14 需要修正,甚至代码是错误的,不能工作,需要修复的说明