1

Android代码规范指北

 2 years ago
source link: http://blog.agilestudio.cn/Android-Code-Rules/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

AgileStudio博客

独立开发者/技术分享/自由职业/外包软件定制

Android代码规范指北

文章作者: 叶大侠

统一的代码风格在多人协作开发中的作用是不言而喻的,通过参考一些比较优秀的实践,这里大量参考了阿里巴巴 JAVA 开发手册,再结合了个人的思考,制定了这么一套规范,由于个人的认识是非常有限的,本规范也肯定存在很多不合理和需要补充的东西,在这里恳请大家根据自己的实践和工作中,提出一些中肯的建议和修改意见。

  1. 高度有秩序的代码具有天然的美感,写代码的人心情会好很多。
  2. 减低开发人员流动带来的风险。
  3. 对于新人能更好地理解和参与到开发中。
  4. 更好的可维护性。
  5. 随之带来更好的产品质量和开发效率提升。
  • 【强制】所有编程相关命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
  • 【强制】所有编程相关的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
  • 【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
  • 【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
  • 【强制】相关组件类应该以组件名作为后缀以便识别。

组件 命名规则 命名举例

Activity ×××Activity MainActivity

Fragment ×××Fragment HomeFragment

Dialog ×××Dialog AlertDialog

Service ×××Service DownloadService

BroadcastReceiver ×××Receiver LoginReceiver

ContentProvider ×××Provider UserProvider

Adapter 名字+类型+Adapter ArticleListAdapter, ImageGridAdapter

AsyncTask ×××Task LoginTask

Handler 名字+所在线程+Handler HomeUIHandler, CompressWorkHandler

ViewHolder VH + 名字 VHArticle

  • 【推荐】实用工具类命名成 **Utils**Helper
  • 【推荐】EventBus 发布的事件名命名成以 Event 结尾,比如 LoginEvent
  • 【推荐】如果使用到了设计模式,建议在类名中体现出具体模式。
  • 【推荐】接口一般以大写 I 开头,回调的接口一般为 Listener 或者 Callback 结尾。
  • 【强制】POJO 类中的任何布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
    ==反例==:定义为基本数据类型 boolean isSuccess; 的属性,它的方法也是 isSuccess() ,部分Json 框架在反向解析的时候,“以为”对应的属性名称是 success,导致属性获取不到,进而抛出异常。
  • 【强制】EventBus 回调的方法必须和相关 Event 类一致,比如LoginEvent类的方法签名为: public final void onLoginEvent(LoginEvent e);
  • 【推荐】客户端逻辑大部分是基于事件驱动的,应该以on来开头,比如登录按钮的点击可以是:onLoginClick()
  • 【强制】常量命名全部大写,单词间用下划线隔开。
  • 【强制】局部变量和一般类变量以小写字母开头。
  • 【推荐】static 类变量名称以s开头,final类变量用f开头。
  • 【推荐】组件相关的可以用【全部首字母+名字】的命名,比如 id 是 R.id.tv_login 的控件名称是:tvLogin

资源文件命名

除了 attr 和 style 资源遵循驼峰命名之外,其他资源的命名统一用小写字母+下划线的风格。

  • 【推荐】布局文件命名

布局类型 命名规则 例子

Activity activity_××× HomeActivity 对应 activity_home

Dialog dlg_××× LoginDialog 对应 dlg_login

Fragment frag_××× HomeFragment 对应 frag_home

页面标题 title_××× title_main

列表Footer footer_××× footer_article

列表Header header_××× header_article

列表的Item item_××× item_article

可重用嵌入布局 include_ ××× include_navagator

分割线 line_+颜色+大小 line_gray_1px

  • 【推荐】id命名:截取相关组件首个字母作为开头,然后以下划线作为分割。比如【TextView:tv_name】。
  • 【推荐】图片命名:【类别+名称+状态(如果有)】,根据图片的用途可以分类为图标「ic」,背景「bg」,前景「fg」(比较少用到),图片「img」(比如引导图,开机页面),如果有多个状态用同一张图,那么首选声明为 【××_selected】 。

状态 示例

正常状态 ic_login_primary

checked ic_login_checked

pressed ic_login_pressed

selected ic_login_selected

disable ic_login_disable

  • 【推荐】drawable命名
  1. xml定义图形:参考图片命名规则,在以上基础上添加辨识,比如: bg_login_shape, bg_login_vector, bg_login_shape_primary。
  2. 多个状态drawable: 命名为 【××_selector】,比如 bg_login_selector。
  • 【推荐】颜色命名
  1. 单色:命名规则为【类型+名字+状态(如果有)】,我把用到颜色的元素类型做了个归类,分别是字体「text」,背景「bg」,线「line」,还是一些常规色(比如白色,透明等),正例:text_black_primary, bg_black_selected, line_orange。
  2. 字体selector颜色:【名字 + text_selector】 ,比如 tab_item_text_selector。
  • 【推荐】尺寸命名:通用性比较强和需要适配的在 dimens.xml 中设置,一般的可以在代码中直接写。
  1. 字体大小: 【font_××】,××和设计稿保持一致的换算。
  2. 间距:组件外用【××_margin】,组件内用【××_padding】, 例子: app_left_margin。
  3. 控件大小:【宽度:××_width】,【高度:××_height】,其他【××_size】,例子: login_button_height。
  • 【推荐】其他:文字不得直接写到 java 代码和 layout 代码里面,应该抽离到相关的 strings 资源文件中;menu 和动画资源命名不需要加前缀或者后缀,能表达意图即可,因为 R.menu 或者 R.anim 已经携带了相关的信息。
  • 【强制】大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果
    是非空代码块则:
  1. 左大括号前不换行。
  2. 左大括号后换行。
  3. 右大括号前换行。
  4. 右大括号后还有 else 等代码则不换行;表示终止右大括号后必须换行。
  • 【强制】 左括号和后一个字符之间不出现空格;同样,右括号和前一个字符之间也不出现空格; 代码块缩进 4 个空格,如果使用 tab 缩进,请设置成 1 个 tab 为 4 个空格。
正例:
2
public static void main(String args[]) {
3
    // 缩进 4 个空格
4
    String say = "hello";
5
    // 运算符的左右必须有一个空格
6
    int flag = 0;
7
    // 关键词 if 与括号之间必须有一个空格,括号内 f 与左括号,1 与右括号不需要空格
8
    if (flag == 0) {
9
        System.out.println(say);
    }
    // 左大括号前加空格且不换行;左大括号后换行
    if (flag == 1) {
        System.out.println("world");
    // 右大括号前换行,右大括号后有 else,不用换行
    } else {
        System.out.println("ok");
    // 右大括号做为结束,必须换行
    }
20
}
  • 【强制】任何运算符左右必须加一个空格。

说明:运算符包括赋值运算符 =、逻辑运算符 &&、加减乘除符号、三目运行符等。

  • 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。
  • 【强制】单行字符数限制不超过 120 个,超出需要换行,换行时,遵循如下原则:
  1. 换行时相对上一行缩进 4 个空格。
  2. 运算符与下文一起换行。
  3. 方法调用的点符号与下文一起换行。
  4. 在多个参数超长,逗号后进行换行。
  5. 在括号前不要换行,见反例。
正例:
2
StringBuffer sb = new StringBuffer();
3
//超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点符号一起换行
4
sb.append("zi").append("xin")…
5
    .append("huang");
6
7
反例:
8
StringBuffer sb = new StringBuffer();
9
//超过 120 个字符的情况下,不要在括号前换行
sb.append("zi").append("xin")…append
    ("huang");
//参数很多的方法调用也超过 120 个字符,逗号后才是换行处
method(args1, args2, args3, ...
    , argsX);
  • 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。

正例:下例中实参的 “a” ,后边必须要有一个空格。method("a", "b", "c");

  • 【强制】在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用下面的形式:if (condition) statements;

  • 【推荐】推荐尽量少用 else, if-else 的方式可以改写成:

if(condition){
2
3
    return obj;
4
} 
5
说明:如果使用要 `if-else if-else` 方式表达逻辑,【强制】请勿超过 3 层,超过请使用状态设计模式。
  • 【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外),遍历长度的提前获取;避免使用 foreach,迭代器,尽量采用下标的形式
正例:
2
(for i=0 ;i <size; ++i)
3
4
反例:
5
for(int i : items)

OOP 规约

  • 【强制】过时的类或者方法不要使用,覆盖的方法要用Override声明。这样当写错覆盖方法时可以得到编译错误提示。
  • 【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
  • 【强制】单例或者实用类构造方法应该声明为 private。
  • 【强制】如果用到 FastJson 来解析数据,必须在相关 getter 和 setter 方法上添加注解。
  • 【推荐】声明为 public 的方法要对参数进行校验,private 的可以不用。
  • 【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便于阅读。
  • 【推荐】 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter 方法。
  • 【推荐】循环体内,字符串的联接方式,使用 StringBuilder 的 append 方法进行扩展。
  • 【推荐】类成员与方法访问控制从严:
  1. 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
  2. 工具类不允许有 public 或 default 构造方法。
  3. 类非 static 成员变量并且与子类共享,必须是 protected。
  4. 类非 static 成员变量并且仅在本类使用,必须是 private。
  5. 类 static 成员变量如果仅在本类使用,必须是 private。
  6. 若是 static 成员变量,必须考虑是否为 final。
  7. 类成员方法只供类内部调用,必须是 private。
  8. 类成员方法只对继承类公开,那么限制为 protected。
  • 【强制】不允许出现任何魔法值(即未经定义的常量)直接出现在代码中。
  • 【强制】long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l ,小写容易跟数字 1 混淆,造成误解。
  • 【推荐】不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如:缓存
    相关的常量放在类:CacheConsts 下;系统配置相关的常量放在类:ConfigConsts 下。
  • 【推荐】避免使用枚举类型,用@IntDef进行替代。
  • 【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
    反例:
    2
    void setServerPort(String value) {
    3
        try {
    4
            serverPort = Integer.parseInt(value);
    5
        } catch (NumberFormatException e) { }
    6
    }
  • 【强制】异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
  • 【强制】finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
  • 【强制】不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
  • 【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。==说明==:如果预期抛的是绣球,实际接到的是铅球,就会产生意外情况。
  • 【推荐】不要偷懒直接捕获顶级异常,这样会把Runtime的异常也囊括进来,要明确每种异常出现的场景被给出相应的处理。
    反例:
    2
    try {
    3
        someComplicatedIOFunction();        // may throw IOException
    4
        someComplicatedParsingFunction();   // may throw ParsingException
    5
        someComplicatedSecurityFunction();  // may throw SecurityException
    6
        // phew, made it all the way
    7
    } catch (Exception e) {                 // I'll just catch all exceptions
    8
        handleError();                      // with one generic handler!
    9
    }
  • 【推荐】除非有充分的理由,不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类,如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。
  • 【强制】类、类属性、类方法的注释必须使用 javadoc 规范,使用/*内容/格式,不得使用//xxx 方式。
  • 【强制】所有的抽象方法(包括接口中的方法)必须要用 javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
  • 【强制】所有的类都必须添加创建者信息。
  • 【强制】对那些临时性的、短期的、够棒但不完美的代码,请使用TODO注释。
    // TODO: Change this to use a flag instead of a constant.
  • 【强制】方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
    void method(){
    2
        // 打个招呼
    3
        sayHello();
    4
    5
        /*
    6
          这里保持移动,
    7
          并不断对步数加1
    8
         */
    9
        keepMove();
    }
  • 【参考】好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一
    个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。

Android 实践

  • 【强制】避免对同个 View 进行多次查找,应该缓存起来,比如列表中使用的 ViewHolder 模式。
反例:
2
view.findViewById(R.id.iv_toolbar_right).setOnClickListener(this);
3
view.findViewById(R.id.iv_toolbar_right).setEnabled(true);
  • 【强制】不要使用 System.out.println() ,printf() 打印日志。
  • 【强制】调试日志要有个开关,在正式发布的时候不能出现应用日志打印。
  • 【强制】读文件,数据库,网络请求等一些耗时操作必须在异步线程中执行。
  • 【强制】不能粗暴的把 Activity, Service 对象或者一些比较重的对象直接声明为 static 变量,如果必须这么做,请注意它们的生命周期变化。
  • 【强制】每引入一个第三方库,要检查是否加入相关的混淆规则。
  • 【强制】自定义属性动画 (ObjectAnimator) 的 getter 和 setter 方法时要注意防止混淆。
  • 【强制】在 (Activity,Fragment) 组件退出时要保证相关资源得到释放,比如 EventBus 订阅的事件,注册的广播,轮播任务等。
  • 【强制】对于Api Level大于等于23的设备要进行运行时权限检查。
  • 【推荐】在使用 Thread,Handler,AsyncTask,Timer,耗时的匿名回调类,内部类时,考虑声明成静态的内部类,并且弱引用或者软引用来处理相关的大对象,并且要注意生命周期的变化,避免出现内存泄漏问题。
  • 【推荐】利用 TextView 的drawableLeft等来减少 ImageView 的使用。
  • 【推荐】通过使用 RelativeLayout 或者 ConstraintLayout 来减少布局的层次。
  • 【推荐】在 findViewById 尽可能选取一个比较近的父节点来减少查找时间。
  • 【推荐】Bundle 中的常量 Key 值统一在一个文件中,而不是直接在 Activity 或者 Fragment 中,比如 BundleKeys 文件中。
  • 【推荐】可滑动列表复用的 ViewHolder 对象中,对于一些事件对象应该尽可能地复用。
  • 【推荐】对隐式 Intent 的运行时调用 resolveActivity 进行检查保护。
  • 【推荐】使用 NotificationCompat 兼容包来处理消息通知。
  • 【推荐】延迟创建没有用到的对象,比如 Adapter 的列表对象,应该在请求数据取到之后再去创建,而不是提前创建好。
  • 【推荐】使用 ArrayMap,Sparse×× 系列对象来减少内存消耗和避免AutoBoxing。
  • 【推荐】更新列表局部数据避免粗暴地调用 Adapter.notifyDataSetChanged,应该只更新变化的部分。
  • 【推荐】用 @Nullable, @NonNull, @IdRes, @MainThread 等Support Annotations注解来增强代码的编译期检查和可读性。
  • 【参考】如果不用getColor(int id, Theme theme)等相关方法,至少使用 ContextCompat 来获取相关 Color 或者图片资源。
  • 【参考】尽可能通过 Fragment 来实现视图层,Activity 只是负责嵌入和管理。
  • 【参考】通过 Picasso,Glide 等开源组件去加载和缓存你的图片。

关于Agile Studio工作室

我们是一支由资深独立开发者和设计师组成的团队,成员均有扎实的技术实力和多年的产品设计开发经验,提供可信赖的软件定制服务。

未经声明,本站文章均为原创,转载请附上链接:
http://blog.agilestudio.cn/Android-Code-Rules/


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK