Android代码规约
对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率,高效协作即降低协同成本。所谓无规矩不成方圆,无规范不能协作。车同轨,书同文,规约是我们高效合作的基础。代码的字里行间流淌的是软件生命中的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升质量意识。
工程师对于代码,一定要“精益求精”,不论从性能,还是简洁优雅,都要具备“精益求精”的工匠精神,认真打磨自己的作品。一个优秀的工程师和一个普通工程师的区别,不是现在满天飞的架构图,他的功底就是体现在他写的每一行代码上。
正例:requestVideoLiveComment( ),defaultHead;
反例:requestVideoDanmu( ),defultHead,queryJizhaoEntrance()
正例:mTitleBar,ExpectJobBean,isUnpublished;
反例:mtitleBar,ExpectjobBean,isUnPublished;
正例:SearchReqBean,DeliverListBean,SearchExposurePositionBean;
反例:ConditionResVo,DeliverHistoryParams,ExposurePositionInfo,IDObject。
命名以它要测试的类的名称开始,以 Test 结尾。
正例:com.alpha.lagouapk.bean, com.alpha.lagouapk.ui.redpacket
反例:com.alpha.lagouapk.ui.login.guidePerfect, com.alpha.lagouapk.ui.rebuild_login
正例:TYPE_COLLECTED = 1 ;
反例:TYPE_1 = 1 ;
类型 |
约定命名 |
不规范做法 |
个人开发分支 |
ylwang_764_effect_guarantee 开发者_版本号_需求英文名 含义:永良-764版本-效果保障 |
ylwang_xiaoguobaozhang ylwang_764 ylwang_02.24 |
单功能分支(protected) |
feature_764_effect_guarantee |
命名同上 严禁把无法运行代码合并到非个人开发分支 |
个人合并发版分支 |
ylwang_release_764 (release_764+ feature_764_effect_guarantee) |
不要把release合并到feature,用发版分支污染单功能分支 |
发版分支 |
release_764 |
|
sub分支 |
开发者/feature_版本号_需求英文名 |
sub分支 必须与其对应主分支同步合并。 |
注意:业务开发分支在远端保存时间不宜超过45天,(2周/迭代,超过两个迭代的业务分支建议及时清理,技术探索分支除外)
流程示意图:
正例 :
public static final int TYPE_ONLINE_POSITION = 2;//在线职位类型
switch (type) {
case TYPE_ONLINE_POSITION:
break;
.....
default:
break;
}
反例:if (2 == type) {} ,//数值 ①语意不明,②定位和改动时代价较大。
getIntent().getStringExtra("positionIds")//key ①put/get均拼写易手误,②不宜追踪
说明:Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。
正例:TYPE_HOME.equals(bean.getTab());
反例:bean.getCity().equals(BEI_JING);//city为空时,空指针异常
Activity 的 layout 以 activity_module 开头
Fragment 的 layout 以 fragment_module 开头
Dialog 的 layout 以 dialog_module 开头
ListView/RecyclerView的行 layout 以 item_list_module开头
自定义View:DefaultEmptyLayout--> layout_default_empty.xml
正例:
场景 |
示例图 |
约定命名 |
说明 |
不规范命名 |
文本框单色填充背景 |
shape_3300b38a_r4.xml |
1.文本框 不支持点击效果。 2.类型_颜色值_r圆角值.xml |
bg_green_cornor_4.xml item_list_click_bg.xml |
|
文本框空心线条背景 |
shape_stroke_8cedcf93_r2.xml |
类型_stroke_颜色值_r圆角值.xml |
bg_ffffff_stroke_corner5.xml |
|
按钮单色填充背景 |
selector_00b38a_r4.xml |
按钮支持点击效果:包含pressed,disable等状态 |
||
按钮双色背景 |
selector_f2fbf9_00b38a_r4.xml |
按照 default颜色 先内后外的顺序注明,类型_主色值_次色值_r圆角值.xml |
||
非全圆角背景 |
selector_ffffff_00b38a_tl_r4.xml |
左上tl,右上tr,左下bl,右下br |
||
shape_edcf93_tl_bl_r10.xml |
左上和左下 有圆角,右侧无圆角 |
正例:
反例:
正例:
【推荐】Id 资源原则上以驼峰法命名,View 组件的资源 id 需要以 View 的缩写作为前缀。
组件缩写_页面名_职责,如职位详情页-职位名 tv_jd_position_name / tvPositionName
常用缩写表如下:
控件 |
资源命名 |
控件命名 |
TextView |
tv_jd_position_name tv_jd_hr_name |
tvPositionName tvHrName |
Button |
btn_collect_send btn_collect_cancel |
btnSend btnCancel |
ImageView |
iv_im_company_logo iv_im_hr_header |
ivCompanyLogo ivHrHeader |
ConstrantLayout |
cl_publish_root cl_deliver_info |
clRoot clInfo |
LinearLayout |
ll_publish_root |
llRoot |
EditText |
et_resume_experience |
etExperience |
ViewPager |
vp_position_list |
vpPositions |
RecyclerView |
rv_home_recommend |
recyclerViewRecommend |
其它控件的缩写推荐使用小写字母并用下划线进行分割,例如:
ProgressBar 对应的缩写为 progress_bar
DatePicker 对应的缩写为 date_picker
//超长反例
CommonNetManager.requestPositionMark(String.valueOf(mData.getPositionId()), new OnAdapterCommonListenerImpl
if (recyclerView != null && recyclerView.getAdapter() != null && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == recyclerView.getAdapter().getItemCount() && !isFetchingData) {
正例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行
sb.append("https:").append("//")...
.append("lagou")...
.append(".")...
.append("com");
反例:
StringBuffer sb = new StringBuffer();
// 超过 120 个字符的情况下,不要在括号前换行
sb.append("剩余").append("25")...append
("个职位");
// 参数很多的方法调用可能超过 120 个字符,不要在逗号前换行
method(args1, args2, args3, ...
, argsX);
说明:
Android 应用页面上任何一个 View 都需要经过 measure、layout、draw 三个步骤才能被正确的渲染。从 xml layout 的顶部节点开始进行 measure,每个子节点都需要向自己的父节点提供自己的尺寸来决定展示的位置,在此过程中可能还会重新measure(由此可能导致 measure 的时间消耗为原来的 2-3 倍)。节点所处位置越深,套嵌带来的 measure 越多,计算就会越费时。这就是为什么扁平的 View 结构会性能更好。同时,页面拥有的 View 越多,measure、layout、draw 所花费的时间就越久。要缩短这个时间,关键是保持 View 的树形结构尽量扁平,而且要移除所有不需要渲染的View。理想情况下,总共的 measure,layout,draw 时间应该被很好的控制在 16ms以内,以保证滑动屏幕时 UI 的流畅。要找到那些多余的 View(增加渲染延迟的 view),可以用 Android Studio Monitor里的 Hierarachy Viewer 工具,可视化的查看所有的 view。多重嵌套导致 measure 以及 layout 等步骤耗时过多。
if (condition) {
……
return obj;
}
// 接着写 else 的业务逻辑代码;
躲不开if...else if...else 时,不宜超过3层,建议使用“卫语句”代替
说明:没有必要插入多个空行进行隔开。
说明:AS中倒序快捷键 list.forr + 回车
说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。注意:数组修改,集合也会跟着变化。
说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是
一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。
说明:HashMap 使用 HashMap(int initialCapacity) 初始化, 正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建 hash 表,严重影响性能。
正例
private void setData(int currIndex) {
int safeIndex = Math.max(0, currIndex) % positionList.size();
PositionBean positionBean = positionList.get(safeIndex);
}
反例:list.remove( index);
int index;
int value;
……
ArrayList
idsList.remove(index % idsList.size());//按照下标删除
idsList.remove((Integer) value);//按照数值删除元素
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。牵涉频繁创建销毁开销时要有池化意识。
说明:嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能,
Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:
非必须的场景尽可能避免使用嵌套 Fragment,如需使用请注意上述问题。
说明:
Activity 可 能 因 为 各 种 原 因 被 销 毁 , Android 支 持 页 面 被 销 毁 前 通 过Activity#onSaveInstanceState() 保 存 自 己 的 状 态 。 但 如 果FragmentTransaction.commit()发生在 Activity 状态保存之后,就会导致 Activity 重建、恢复状态时无法还原页面状态,从而可能出错。为了避免给用户造成不好的体验,系统会抛出 IllegalStateExceptionStateLoss 异常。推荐的做法是在 Activity 的onPostResume() 或 onResumeFragments() ( 对 FragmentActivity )里执行FragmentTransaction.commit(),如有必要也可在 onCreate()里执行。不要随意改用FragmentTransaction.commitAllowingStateLoss() 或 者 直 接 使 用 try-catch 避 免crash,这不是问题的根本解决之道,当且仅当你确认 Activity 重建、恢复状态时,本次 commit 丢失不会造成影响时才可这么做。
说明:
说明:
ScrollView 中嵌套 List 或 RecyclerView 的做法官方明确禁止。除了开发过程中遇到的各种视觉和交互问题,这种做法对性能也有较大损耗。ListView 等 UI 组件自身有垂直滚动功能,也没有必要再嵌套一层 ScrollView。目前为了较好的 UI 体验,更贴近 Material Design 的设计,推荐使用NestedScrollView。
说明:
Android 应用提供内部和外部存储,分别用于存放应用自身数据以及应用产生的用户数据。可以通过相关 API 接口获取对应的目录,进行文件操作。
android.os.Environment#getExternalStorageDirectory()
android.os.Environment#getExternalStoragePublicDirectory()
android.content.Context#getFilesDir()
android.content.Context#getCacheDir()
// 读/写检查
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
// 只读检查
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
说明:
SharedPreference 相关修改使用 apply 方法进行提交会先写入内存,然后异步写入磁盘,commit 方法是直接写入磁盘。如果频繁操作的话 apply 的性能会优于 commit,apply 会将最后修改内容写入磁盘。但是如果希望立刻获取存储操作的结果,并据此做相应的其他操作,应当使用 commit。
说明:
Android 应用使用 Intent 机制在组件之间传递数据,如果应用在使用 getIntent(),getAction(),Intent.getXXXExtra()获取到空数据、异常或者畸形数据时没有进行异常捕获,应用就会发生 Crash,应用不可使用(本地拒绝服务)。恶意应用可通过向受害者应用发送此类空数据、异常或者畸形数据从而使应用产生本地拒绝服务。
final FadeUpAnimation anim = new FadeUpAnimation(v);
anim.setInterpolator(new AccelerateInterpolator());
anim.setDuration(1000);
anim.setFillAfter(true);
new Handler().postDelayed(new Runnable() {
public void run() {
v.clearAnimation();
//Extra work goes here
}
}, anim.getDuration());
v.startAnimation(anim);
说明:RGB_565 能够在保证图片质量的情况下大大减少内存的开销,是解决 oom 的一种方法。但是一定要注意 RGB_565 是没有透明度的,如果图片本身需要保留透明度,那么就不能使用 RGB_565。
团队协作中注释和代码一样重要。除非你写的代码,无需解释,任何人一眼就能看明白。
说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
说明:对子类的实现要求,或者调用注意事项,请一并说明。
说明:代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
另外业务逻辑杂糅时,注释应①②③分条说明。
正例:
说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。