Android的开发文档规范

Android的开发文档规范

我们项目的代码时间时间很长,经过太多人手,代码的规范性堪忧,目前存在较多的比较自由的「代码规范」,这非常不利于项目的维护,代码可读性也不够高。
分析现有项目的代码的情况,输出的『定制化规范』文档,用于提高代码的可读性和可维护性。

目的

  • 对于个人:帮助团队写「正确」的代码,提升编程能力。
  • 团队内部:统一项目的编码风格,降低维护『非自己模块』的成本
  • 对外部门:交付更加稳定的产品,并降低后期的维护难度

准备及通用

开发工具配置规范

工欲善其事,必先利其器。

  • 推荐使用最新的稳定版本的 Android Studio 进行开发

  • 编码格式必须统一为UTF-8

  • 编辑完 .java、.kt、.xml 等文件后必须格式化(需要在设置好以下几点的前提下,Reformat Code 的必要性,一定需要保证 IDE 配置一致为前提,尽可能贴切于 Android Studio 默认。强烈建议对于比较长的老代码局部格式化,不全局格式化)

  • 每行字符数不得超过100字符(IDE默认)
    设置 Editor -> Code Style
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nO9cwzzo-1615199362175)(media/16104216402413/iShot2021-03-04%2014.11.54.png)]

  • Java代码格式规则(默认设置)

全部设置为单类导入

在这里插入图片描述

  • XML代码格式规则(默认设置)

在这里插入图片描述

  • 格式化快捷键(默认设置)

以上几处设置完毕,其他采用 Android Studio 默认方式,再进行 Reformat Code 快捷键即可。

在这里插入图片描述

  • 可以安装Alibaba Java开发规范插件,进行代码规范的检查

地址:https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines

代码格式

  • 【强制】提交经过格式化的代码
  • 【强制】单行代码不要超过IDE右边界,单行字符数限制不超过100个(默认设置)
    如果超出行长,我们通常有两种方法来缩减行长。
    1.提取一个局部变量或方法(最好)
    2.使用换行符将一行换成多行(参考『换行策略』)

例外情况:
1.如果备注行包含长度超过 100 个字符的示例命令或文字网址,那么为了便于剪切和粘贴,该行可以超过 100 个字符。
2.导入语句行可以超出此限制,因为用户很少会看到它们(这也简化了工具编写流程)。

  • 【强制】方法、参数、变量命名使用小骆驼拼写法(lowerCamelCase)
  • 【强制】方法之间需要空1行
  • 【强制】杜绝完全不规范的缩写,避免望文不知义。比如:condition“缩写”命名成 condi,英文拼音混合
  • 【强制】如果使用到了设计模式,建议在类名中体现出具体模式。
  • 【强制】方法调用的点符号与下文一起换行,在多个参数超长,在逗号后换行

换行策略(操作符"之前"换行,而逗号"之后"换行。)

这没有一个准确的解决方案来决定如何换行,通常不同的解决方案都是有效的,但是有一些规则可以应用于常见的情况。

操作符的换行

赋值操作符的换行我们放在其后,例如:

int longName =
        anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne + theFinalOne;

除赋值操作符之外,我们把『换行符』放在『操作符』之前,例如:

int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
        + theFinalOne;

函数链的换行

当同一行中调用多个函数时(比如使用构建器时),对每个函数的调用应该在新的一行中,我们把换行符插入在 . 之前。

例如:

Picasso.with(context).load("https://blankj.com/images/avatar.jpg").into(ivAvatar);

我们应该使用如下规则:

Picasso.with(context)
        .load("https://blankj.com/images/avatar.jpg")
        .into(ivAvatar);

多参数的换行

当一个方法有很多参数或者参数很长的时候,我们应该在每个 , 后面进行换行。

例如:

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);

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;
                 }
            });
}
  • 【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用『单数形式』,但是类名如果有复数含义,类名可以使用『复数形式』。
    正例: 应用工具类包名为com.alibaba.open.util、类名为MessageUtils(此规则参考 spring 的框架结构)
  • 【参考】枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线隔开。

OOP 规约

  • 【推荐】类成员的顺序,没有唯一的正确解决方案。但如果都使用一致的顺序将会提高代码的可读性,推荐使用如下排序:常量>属性>构造函数>set/get方法>公有函数>私有函数>内部类或接口
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 或 Fragment),那么把重写函数按照他们的生命周期进行排序是一个非常好的习惯,例如,Activity 实现了 onCreate()、onDestroy()、onPause()、onResume(),它的正确排序如下所示:

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() {}
}
  • 【推荐】类成员与方法访问控制从严:
  1. 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
  2. 工具类不允许有public或default构造方法。
  3. 类非static成员变量并且与子类共享,必须是protected。
  4. 类非static成员变量并且仅在本类使用,必须是private。
  5. 类static成员变量如果仅在本类使用,必须是private。
  6. 若是static成员变量,必须考虑是否为final。
  7. 类成员方法只供类内部调用,必须是private。
  8. 类成员方法只对继承类公开,那么限制为protected。
  • 【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读。
  • 【推荐】setter 方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名。『getter/setter 方法中,不要添加业务逻辑,业务逻辑可以放在其他工具类』
  • 【推荐】循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
  • 【推荐】函数参数的排序,在 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);

注释规范

  • 【极好】类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用 //xxx 方式。
  • 【强制】所有的抽象方法(包括接口中的方法)必须要用Javadoc注释,返回值、参数、异常都需要有注释,还必须指出该方法做什么事情,实现什么功能。
  • 【强制】所有的枚举类型字段必须要有注释,说明每个数据项的用途。
  • 【强制】正确使用注释(//,/**/)
  • 【强制】注释要规范,避免尾行注释

Java代码

变量的命名和值

  • 【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
  • 【推荐】使用Android特有常量的命名规范,参考『Android特有常量』
  • 【推荐】一行代码只声明一个变量,不要有多个变量
  • 【强制】视图变量规则:类型缩写(小写)+业务名称,如:btnClose,tvTitle(参考『常见的视图控件缩写表』)
  • 【推荐】视图变量的『声明顺序』要尽量和『XML布局』里的摆放顺序一致
  • 【强制】中括号是数组类型的一部分,数组定义如下:String[] args;
  • 【强制】不建议魔法值(即未经定义的常量)直接出现在代码中,如果没有抽取,相关代码处需写清楚『注释』。
  • 【强制】long或者Long初始赋值时,必须使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
  • 【推荐】常量的复用层次,类常量<包常量<模块常量,优先使用作用域小的常量。

Android特有常量

Android SDK 中的很多类都用到了键值对函数,比如SharedPreferences、Bundle、Intent,所以,即便是一个小应用,我们最终也不得不编写大量的字符串常量。当时用到这些类的时候,我们『必须』将它们的键定义为 static final 字段,并遵循以下指示作为前缀。

字段名前缀
SharedPreferencesPREF_
BundleBUNDLE_
Fragment ArgumentsARGUMENT_
Intent ExtraEXTRA_
Intent ActionACTION_

例如:

// 注意:字段的值与名称相同以避免重复问题
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";

常见的视图控件缩写表

UI控件缩写
LinearLayoutll
RelativeLayoutrl
FrameLayoutfl
Buttonbtn
ImageButtonibtn
TextViewtv
ImageViewiv
RecyclerViewrv
ConstraintLayoutcl
ScollViewsv
CheckBoxcb
RadioButtonrb
EditTextet

方法的内部实现

  • 【强制】尽量使用lambda代替匿名内部类
  • 【极好】方法的参数和返回值,添加@NonNull,@Nullable注解
    (包含其他androidx.anotation)
  • 【强制】无用的代码直接删除(检测:各自模块无用代码直接删除)
  • 【强制】不要任意添加try-catch,如果添加try-catch需要明确异常
  • 【强制】不要在方法内部修改参数的值(a方法调用,会影响后面的b方法)
  • 【推荐】单个方法实现不超过屏幕(80行,缩短代码行数,做好拆分)
  • 【推荐】方法内部不需要空行
  • 【推荐】不同业务View设置不同事件监听(例如OnClickListener)
  • 【推荐】if-else不要写反向逻辑
  • 【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成:if (condition) { return ; }
  • 【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
    正例: “test”.equals(object);
    反例: object.equals(“test”);
  • 【强制】不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用 Iterator方式。
  • 【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
  • 【强制】在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码,避免使用 单行的形式:if (condition) statements;
  • 【推荐】除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

Android代码

Android基本组件

  • 【强制】Activity间通过隐式Intent的跳转,在发出Intent之前必须通过resolveActivity 检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。
  • 【强制】Activity和Fragment的参数要使用@Autowired(原因:减少模板代码,支持ARouter跳转)
  • 【强制】避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作, 应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。
  • 【强制】如果广播仅限于应用内,则可以使用LocalBroadcastManager.sendBroadcast()实现,避免敏感信息外泄和Intent拦截的风险。
  • 【推荐】优先考虑EventBus等框架代替Broadcast(如果操作轻量级,不涉及Intent和Context的情况下)
  • 【推荐】当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate 方法,所以在onPause方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
  • 【推荐】使用 Toast 时,建议定义一个全局的Toast对象,这样可以避免连续显示Toast 时不能取消上一次Toast消息的情况(如果你有连续弹出Toast的情况,避免使用Toast.makeText)。
  • 【强制】Activity或者Fragment中动态注册BroadCastReceiver时registerReceiver()和unregisterReceiver()要成对出现。否则可能导致已经注册的receiver没有在合适的时机注销,导致内存泄漏,占用内存空间,加重SystemService负担。
  • 【强制】同一方法禁止多次调用sp的apply(一些SPUtils只封装单次的提交,连续调用这样的方法就有这个问题)
  • 【强制】在Activity中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非 Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期。
  • 【推荐】不能在 Activity 没有完全显示时显示 PopupWindow 和 Dialog

进程、线程与消息通信

  • 【强制】新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。(需要线程池)
  • 【强制】线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor 的方 式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
  • 【推荐】ThreadPoolExecutor 设置线程存活时间(setKeepAliveTime),确保空闲时 线程能被释放。

文件与数据库

  • 【强制】任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。
  • 【强制】当使用外部存储时,必须检查外部存储的可用性。
  • 【强制】应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用 FileProvider。
  • 【推荐】SharedPreference中只能存储简单数据类型(int、boolean、String等), 复杂数据类型建议使用文件、数据库等其他方式存储。
  • 【推荐】SharedPreference 提交数据时,尽量使用 Editor#apply(),而非 Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用Editor#commit()。
  • 【强制】数据库 Cursor 必须确保使用完后关闭,以免内存泄漏。
  • 【强制】多线程操作写入数据库时,需要使用事务,以免出现同步问题。
  • 【推荐】大数据写入数据库时,请使用事务或其他能够提高 I/O 效率的机制,保证执行速度。
  • 【强制】执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(), 不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入风险。

Android资源文件

布局

  • 【推荐】推荐使用ConstraintLayout(必要时可以有内部嵌套),布局中不得使用ViewGroup多重嵌套(ConstraintLayout需要普及讲解)
  • 【强制】禁止在非ui线程进行 view 相关操作。
  • 【强制】禁止在设计布局时多次设置子 view和父view中为同样的背景造成页面过度绘制,推荐将不需要显示的布局进行及时隐藏。
  • 【推荐】灵活使用布局,推荐 Merge、ViewStub 来优化布局,尽可能多的减少 UI 布局层级。
  • 【极好】布局文件可以不用运行,仿真预览(使用tools)

Bitmap、Drawable 与动画

  • 【强制】加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加载,涉及到I 操作,以及CPU密集操作,很可能引起卡顿。
  • 【强制】png 图片使用 tinypng 或者类似工具压缩处理,减少包体积。
  • 【强制】在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执 行的的动画。
  • 【推荐】在动画或者其他异步任务结束时,应该考虑回调时刻的环境是否还支持业务处理。例如 Activity的onStop()函数已经执行,且在该函数中主动释放了资源,此时回调中如果不做判断就会空指针崩溃。
  • 【推荐】尽量减少Bitmap(BitmapDrawable)的使用,尽量使用纯色(ColorDrawable)、渐变色(GradientDrawable)、StateSelector(StateListDrawable)等与Shape结合的形式构建绘图。

Android 资源文件命名与使用

  • 【强制】资源文件需带模块前缀(公用放在base)
  • 【强制】动画资源文件(anim/和animator/)

Android主要包含属性动画和视图动画。属性动画文件需要放在res/animator/目录下,视图动画文件需放在res/anim/目录下。命名规则:{模块名_}{逻辑名称}_动画类型_方向,{}中的内容为可选。

逻辑名称可由多个单词加下划线组成。例如:refresh_progress.xml、market_cart_add.xml、market_cart_remove.xml

动画类型和方向的命名方式举例:

名称说明
fade_in淡入
fade_out淡出
push_down_in从下方推入
push_down_out从下方推出
push_left推向左方
slide_in_from_top从头部滑动进入
zoom_enter变形进入
slide_in滑动进入
shrink_to_middle中间缩小
  • 【强制】颜色资源文件(color/)

color/是专门用于存放颜色相关资源的文件夹。命名规则:类型{_模块名}_逻辑名称。

  • 【强制】图片资源文件(drawable/ 和 mipmap/)

res/drawable/ 目录下放的是位图文件(.png、.9.png、.jpg、.gif)或编译为可绘制对象资源子类型的 XML 文件,而 res/mipmap/ 目录下放的是不同密度的启动图标,所以 res/mipmap/ 只用于存放启动图标,其余图片资源文件都应该放到 res/drawable/ 目录下。

命名规则:类型{_页面名称}{_逻辑名称}{_颜色}

说明:{}中的内容为可选;类型 可以是可绘制对象资源类型。
例如:

名称说明
btn_main_about.png主页关于按键 类型_页面名称_逻辑名称
btn_back.png返回按键 类型_逻辑名称
divider_maket_white.png商城白色分割线 类型_页面名称_颜色
ic_edit.png编辑图标 类型_逻辑名称
bg_main.png主页背景 类型_逻辑名称
btn_red.png红色按键 类型_颜色
btn_red_big.png红色大按键 类型_颜色
ic_avatar_small.png小头像图标 类型_逻辑名称
bg_input.png输入框背景 类型_逻辑名称
divider_white.png白色分割线 类型_颜色
bg_main_head.png主页头部背景 类型_页面名称_逻辑名称
def_search_cell.png搜索页面默认单元图片 类型_页面名称_逻辑名称
ic_more_help.png更多帮助图标 类型_逻辑名称
divider_list_line.png列表分割线 类型_逻辑名称
sel_search_ok.xml搜索界面确认选择器 类型_页面名称_逻辑名称
shape_music_ring.xml音乐界面环形形状 类型_页面名称_逻辑名称

如果有多种形态,如按钮选择器:sel_btn_xx.xml,采用如下命名:

名称说明
sel_btn_xx作用在 btn_xx 上的 selector
btn_xx_normal默认状态效果
btn_xx_pressedstate_pressed点击效果
btn_xx_focusedstate_focused聚焦效果
btn_xx_disabledstate_enabled不可用效果
btn_xx_checkedstate_checked选中效果
btn_xx_selectedstate_selected选中效果
btn_xx_hoveredstate_hovered悬停效果
btn_xx_checkablestate_checkable可选效果
btn_xx_activatedstate_activated激活效果
btn_xx_window_focusedstate_window_focused窗口聚焦效果

注意:使用 Android Studio 的插件 SelectorChapek 可以快速生成 selector,前提是命名要规范。

  • 【强制】 布局资源文件(layout/)

命名规则:模块名_类型_

类型名称说明
Activitypassport_activity_main.xml窗体页面 模块名_类型_页面名称
Fragmentresume_fragment_music.xml音乐片段 模块名_类型_逻辑名称
Dialogresume_dialog_loading.xml加载对话框 模块名_类型_逻辑名称
PopupWindowposition_popup_info.xml信息弹窗(PopupWindow) 模块名_类型_逻辑名称
adapter 的列表项my_item_main_song.xml主页歌曲列表项 模块名_类型_页面名称_逻辑名称
  • 【强制】视图layout布局的命名:view_float_live
  • 【强制】嵌套layout布局的命名:layout_title_bar(一般有复用)
  • 【强制】布局资源 id 命名

命名规则:类型小写_{模块名_}_逻辑名,例如:btn_main_search、btn_back。

  • 【推荐】colors.xml

的name命名使用下划线命名法,在你 colors.xml 文件中应该只是映射颜色的名称一个argb值,而没有其它的。不要使用它为不同的按钮来定argb值。

例如,不要像下面这样做:

 <resources>
      <color name="button_foreground">#FFFFFF</color>
      <color name="button_background">#2A91BD</color>
      <color name="comment_background_inactive">#5F5F5F</color>
      <color name="comment_background_active">#939393</color>
      <color name="comment_foreground">#FFFFFF</color>
      <color name="comment_foreground_important">#FF9D2F</color>
      ...
      <color name="comment_shadow">#323232</color>

使用这种格式,会非常容易重复定义argb值,而且如果应用要改变基色的话会非常困难。同时,这些定义是跟一些环境关联起来的,如 button或者comment,应该放到一个按钮风格中,而不是在 colors.xml 文件中。

应该这样做

  <resources>

      <!-- grayscale -->
      <color name="white"     >#ffffff</color>
      <color name="gray_light">#dbdbdb</color>
      <color name="gray"      >#939393</color>
      <color name="gray_dark" >#5f5f5f</color>
      <color name="black"     >#323232</color>

      <!-- basic colors -->
      <color name="green">#27d34d</color>
      <color name="blue">#2a91db</color>
      <color name="orange">#ff9d2f</color>
      <color name="red">#ff432f</color>

  </resources>

向应用设计者那里要这个调色板,名称不需要跟 “green”、“blue” 等等相同。“brand_primary”、“brand_secondary”、“brand_negative” 这样的名字也是完全可以接受的。像这样规范的颜色很容易修改或重构,会使应用一共使用了多少种不同的颜色变得非常清晰。通常一个具有审美价值的 UI 来说,减少使用颜色的种类是非常重要的。

  • 【强制】styles.xml

style的name命名使用大驼峰命名法,几乎每个项目都需要适当的使用 styles.xml 文件,因为对于一个视图来说,有一个重复的外观是很常见的,将所有的外观细节属性(colors、padding、font)放在 styles.xml 文件中。在应用中对于大多数文本内容,最起码你应该有一个通用的styles.xml 文件,例如:

<resources>
    <style name="ContentText">
        <item name="android:textSize">@dimen/font_normal</item>
        <item name="android:textColor">@color/basic_black</item>
    </style>
</resources>

应用到 TextView 中:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/price"
    style="@style/ContentText"/>
  • 【推荐】style 资源采用小写单词+下划线方式命名,写入 module_styles.xml 文件中, 采用以下规则:父style名称.当前style名称,如ParentTheme.ThisActivityTheme。

参考

  • 阿里巴巴Java开发手册v1.2.0
  • 阿里巴巴Android开发手册
  • Android代码规范大全(https://juejin.cn/post/6921257795555852301)
  • BroadcastReceiver、EventBus的优缺点(https://blog.csdn.net/crazy_yyyyy/article/details/51318092)
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页