From 490a8d3e3695dd30a952ded71969fda83daaa58c Mon Sep 17 00:00:00 2001 From: HJQ <880634@qq.com> Date: Fri, 5 Feb 2021 16:03:49 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 13 + LICENSE | 202 ++++ README.md | 965 ++++++++++++++++++ ...0\345\217\221\346\211\213\345\206\214.pdf" | Bin 0 -> 1301108 bytes ...0\345\217\221\346\211\213\345\206\214.pdf" | Bin 0 -> 1163395 bytes 5 files changed, 1180 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 "\351\230\277\351\207\214\345\267\264\345\267\264Android\345\274\200\345\217\221\346\211\213\345\206\214.pdf" create mode 100644 "\351\230\277\351\207\214\345\267\264\345\267\264Java\345\274\200\345\217\221\346\211\213\345\206\214.pdf" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..70df1fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/.gradle +/.idea +/build +*/build +/captures +/.cxx +*/.cxx +/.externalNativeBuild + +._* +*.iml +.DS_Store +local.properties \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2c6f274 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Huang JinQun + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1ae80c --- /dev/null +++ b/README.md @@ -0,0 +1,965 @@ +# Android 代码规范文档 + +* 开源几年了,被很多人夸过,你的代码写得比较规范,[甚至有人质疑自己代码的写法了](https://github.com/getActivity/AndroidProject/issues/55),但是迟迟没有出一个代码规范,只是因为我早几年写的代码还不够规范,规范是后续才养成的,我不仅参考了大公司出的代码规范文档,也研究了很多关于谷歌源码的编码规范,同时我也在无时不刻在思考,如何能写出让别人更好理解的代码,自打我入行以来,我就在这条路上一直探索。 + +* 有人可能会问了,你代码写得再好有什么用?对业务有什么贡献吗?我随便写也能完成需求,为什么要加那么多条条框框?其实我觉得吧,有一个好的编码规范,能够帮助我们更好地在技术道路上探索,道理也很简单,我们先问自己一个问题,如果让你看一个代码写得乱七八糟的项目,你能看多久?有人肯定会说了,给钱我就干,管它代码烂不烂。假设是没有钱的情况下呢?有的人可能上来一句垃圾项目,然后立马关闭了刚打开的 Android Studio,再假设这是你自己几个月前写的个人项目呢?不要以善小而不为,我们都希望能够被这个世界温柔以待,但是我们自己是否能温柔看待这个世界? + +* 程序员的工作就是写代码,如果仅仅停留在能实现业务的基础上,那么这对个人是没有多大提升的,没有提升意味着我们会陷入迷茫,因为时间在前进,别人也在提升,就剩自己一个人在原地重复踏步。这个时候技术追求显得非常重要了,这将会是决定你超越大多人的精神武器,有人毕业两三年就已经是高级工程师,也有人毕业几年濒临失业,为什么差距那么大?因为距离不是一天两天决定的,而是来源于日积月累。 + +#### 目录 + +* [前言](#前言) + +* [常规规范](#常规规范) + +* [后台接口规范](#后台接口规范) + +* [变量命名规范](#变量命名规范) + +* [包名命名规范](#包名命名规范) + +* [方法命名规范](#方法命名规范) + +* [类文件命名规范](#类文件命名规范) + +* [接口文件命名规范](#接口文件命名规范) + +* [布局文件命名规范](#布局文件命名规范) + +* [资源文件命名规范](#资源文件命名规范) + +* [接口实现规范](#接口实现规范) + +* [异常捕获规范](#异常捕获规范) + +* [Activity 跳转约定](#activity-跳转约定) + +* [第三方框架使用规范](#第三方框架使用规范) + +* [代码注释规范](#代码注释规范) + +* [String ID 命名规范](#string-id-命名规范) + +* [Anim ID 命名规范](#anim-id-命名规范) + +* [View ID 命名规范](#view-id-命名规范) + +* [Style 样式命名规范](#style-样式命名规范) + +* [资源使用规范](#资源使用规范) + +* [XML 编码规范](#xml-编码规范) + +* [预览属性约定](#预览属性约定) + +* [致谢](#致谢) + +#### 前言 + +* 代码规范是我们每个程序员要做的事,假设我们按照自己的喜好来写代码,那么很可能出现的问题就是我看不懂你的代码或者你看不懂我的代码,这样会给后续维护形成巨大的障碍。这个时候问题来了,如何让代码不分你我,或许只需要一套规则,你和我都认可并且遵守的代码规范守则。 + +* 那么你的疑问可能又来了,怎么样才能算好的代码规范,答案只有一个,真正好的代码规范就是别人的代码你一眼就能看懂,更不需要反复去看。之所以这样并不是因为看的人 Review 代码的能力有多强,而是写代码的人愿意遵守规则,他知道自己想这么写,但是知道你会看不懂,所以换了一种方式来写,这种方式就是代码规范。 + +* 代码规范:一个好的代码规范可以帮助我们快速了解和熟悉相关的业务,降低后续维护的成本(二次开发和排查问题) + +* 代码不规范:代码不规范会导致项目可读性变差,具体表现为难分辨难理解,在无形之中加大后续维护的成本 + +* 经验总结:编码不规范,同行两行泪 + +#### 常规规范 + +* 不用 `0dp`,而用 `0px`,这样可以避免系统进行换算,提升代码的执行效率 + +* 尽量采用 `switch case` 来判断,如果不能实现则再考虑用 `if else` + +* 不用 paddingLeft,而用 `paddingStart`;不用 `paddingRight`,而用 `paddingEnd` + +* 不用 `layout_marginLeft`,而用 `layout_marginStart`;不用 `layout_marginRight`,而用 `layout_marginEnd` + +* 如果在 `layout_marginStart` 和 `layout_marginEnd` 相同的情况下,请替换使用 `layout_marginHorizontal`,而 `layout_marginTop` 和 `layout_marginBottom` 也同理,请替换使用 `layout_marginVertical`,`padding` 属性也是同理,这里不再赘述 + +* `过期` 和 `高版本` 的 API 一定要做对应的兼容(包含 Java 代码和 Xml 属性) + +* 在能满足需求的情况下,尽量用 `invisible` 来代替 `gone`,因为 `gone` 会触发当前整个 View 树进行重新测量和绘制 + +* `api` 和 `implementation`,优先选用 `implementation`,因为这样可以[减少一些编译时间](https://www.jianshu.com/p/8962d6ba936e) + +* `ListView` 和 `RecyclerView` 都能实现需求的前提下,优先选用 `RecyclerView` + +* `ScrollView` 和 `NestedScrollView` 都能实现需求的前提下,优先选用 `NestedScrollView` + +* 应用图标应该放在 `mipmap` 目录下,其他图片资源应当放到 `drawable` 目录下,具体原因可以点击[谷歌官方文档](https://developer.android.google.cn/guide/topics/resources/providing-resources)进行查看 + +* 不能在项目中创建副本文件,例如创建 `HomeActivity2.java`、`home_activity_v2.xml` 类似的副本文件,因为这样不仅会增加项目的维护难度,同时对编译速度也会造成一定的影响,正确的做法应该是在原有的文件基础上面修改,如果有需求回退的情况,请使用 `Git` 或者 `SVN` 进行版本回退 + +* 如果一个类不需要被继承,请直接用 `final` 进行修饰,如果一个字段在类初始化过程中已经赋值并且没有地方进行二次赋值,也应当用 `final` 修饰 + +* 每个小组成员应当安装[阿里巴巴代码约束插件](https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines),并及时地对插件所提示的`代码警告`进行处理 + +#### 后台接口规范 + +* 后台返回的 `id` 值,不要使用 int 或者 long 类型来解析,而应该用 `string` 类型来解析,因为我们不需要对这个 id 值进行计算 + +* 后台返回的`金额`应该使用 `double` 来解析,而不能用 float 来解析,因为 float 在数值比较大的时候会容易丢失精度 + +* 我们在定义后台返回的 Bean 类时,不应当将一些我们没有使用到的字段添加到代码中,因为这样会消耗性能,因为 Gson 是通过`反射`将后台字段赋值到 Java 字段中,所以我们应当避免一些不必要的字段解析,另外臃余的字段也会给我们排查问题造成一定的阻碍 + +* 如果后台给定的字段名不符合 Java 代码命名,我们应当使用 `@SerializedName` 注解进行重命名 + +* 请求的接口参数和返回字段必须要写上注释,除此之外还应该备注对应的后台接口文档地址,以便我们后续能够更好地进行维护和迭代 + +* 后台返回的 Bean 类字段不能直接访问,而应该通过生成 Get 方法,然后使用这个 Get 方法来访问字段 + +* 接口请求成功的提示可以不显示,但是请求失败的提示需要要显示给到用户,否则会加大排查问题的难度,也有可能会把问题掩埋掉,从而让问题遗留到线上去 + +#### 变量命名规范 + +* `严禁使用中文或者中文拼音`进行重命名 + +* 使用`驼峰式命名风格`(单词最好控制在三个以内) + +* 局部变量应用作用来命名,示例: + + * `String name` + + * `TextView nameView` + + * `FrameLayout nameLayout` + + * 命名规范附带技巧(当布局中同个类型的控件只有一个的时候,可以这样命名) + + * `TextView textView` + + * `RecyclerView recyclerView` + +* 全局变量必须以小 m 开头,示例: + + * `String mName` + + * `TextView mNameView` + + * `FrameLayout mNameLayout` + + * 命名规范附带技巧(当布局中同个类型的控件只有一个的时候,可以这样命名) + + * `TextView mTextView` + + * `RecyclerView mRecyclerView` + +* 布尔值命名规范,命名不应该以 `is` 开头 + + * 错误写法示例: + + * 全局变量:`private boolean mIsDebug = false;` + + * 局部变量:`boolean isDebug = false;` + + * 正确写法示例: + + * 全局变量:`private boolean mDebug = false;` + + * 局部变量:`boolean debug = false;` + +* 静态变量则用小 s 开头,示例: + + * `static Handler sHandler` + +* 常量则需要用大写,并且用下划线代替驼峰,示例: + + * `static final String REQUEST_INSTALL_PACKAGES"` + +* 有细心的同学可能会发现一个问题,为什么我们最常用的 Glide 和 OkHttp 的源码为什么没有用字母 `m` 来区分局部变量和全局变量?但是谷歌的 SDK 源码和 Support 库就有呢?那究竟是用还是不用呢?这个问题其实很好回答,我们可以先从体量上分析,首先谷歌的开发人员和项目数量肯定是最多的,那么谷歌在这块的探索和研究肯定是多于 Glie 和 OkHttp 的,其次是 Glide 和 OkHttp 的源码都有一个特点,很多类都维持在 1k 行代码左右,而谷歌源码很多类都接近 10k 行代码,例如 Activity 的源码在 API 30 上面有 8.8k 行代码,所以谷歌在这块略胜一筹,如果非要二选一,我选择谷歌的代码风格,并不是说 Glide 和 OkHttp 命名风格不好,是因为或许在未来的某一天,可能会有新的图片请求框架和网络请求框架来代替 Glide 和 OkHttp,但是基本不可能会出现有代替 Android SDK 或者 Support 库的一天 + +* 最后让我们静静地欣赏一下 `Activity` 字段的命名 + +```java +public class Activity { + + public static final int RESULT_CANCELED = 0; + public static final int RESULT_OK = -1; + + private Instrumentation mInstrumentation; + + private IBinder mToken; + private IBinder mAssistToken; + + private Application mApplication; + + /*package*/ Intent mIntent; + + /*package*/ String mReferrer; + + private ComponentName mComponent; + + /*package*/ ActivityInfo mActivityInfo; + + /*package*/ ActivityThread mMainThread; + + Activity mParent; + + boolean mCalled; + + /*package*/ boolean mResumed; + + /*package*/ boolean mStopped; + + boolean mFinished; + boolean mStartedActivity; + + private boolean mDestroyed; + private boolean mDoReportFullyDrawn = true; + private boolean mRestoredFromBundle; + + private final ArrayList mActivityLifecycleCallbacks = + new ArrayList(); + + private Window mWindow; + + private WindowManager mWindowManager; + + private CharSequence mTitle; + private int mTitleColor = 0; + + + final Handler mHandler = new Handler(); + + final FragmentController mFragments = FragmentController.createController(new HostCallbacks()); +} + +``` + +#### 包名命名规范 + +* 不允许包名中携带`英文大写` + +* 包名应该以`简洁的方式`命名 + +* 包名要按照`模块`或者`作用`来划分 + +* 不要在某一包名下放置`一些无关的类` + +#### 方法命名规范 + +* initXX:初始化相关方法,使用 `init` 为前缀标识,如初始化布局 initView + +* isXX:方法返回值为 boolean 型的请使用 `is` 或 check 为前缀标识 + +* getXX:返回某个值的方法,使用 `get` 为前缀标识 + +* setXX:设置某个属性值 + +* handleXX/processXX:对数据进行处理的方法 + +* displayXX/showXX:弹出提示框和提示信息,使用 display/show 为前缀标识 + +* updateXX:更新数据 + +* saveXX:保存数据 + +* resetXX:重置数据 + +* clearXX:清除数据 + +* removeXX:移除数据或者视图等,如 removeView + +* drawXX:绘制数据或效果相关的,使用 `draw` 前缀标识 + +#### 类文件命名规范 + +* 业务模块以 `模块` + `类型` 来命名,例如 + + * HomeActivity.java + + * SettingFragment.java + + * HomeAdapter.java + + * AddressDialog.java + +* 技术模块以类的 `作用` 来命名,例如 + + * CrashHandler.java + + * GridSpaceDecoration.java + + * PickerLayoutManager.java + +#### 接口文件命名规范 + +* 如果是监听事件可以参考 View 的写法及命名: + +```java +public class View { + + private View.OnClickListener mListener; + + public void setOnClickListener(OnClickListener listener) { + mListener = listener; + } + + public interface OnClickListener { + + void onClick(View v); + } +} +``` + +* 如果是回调事件可以参考 Handler 的写法及命名: + +```java +public class Handler { + + public interface Callback { + + boolean handleMessage(Message msg); + } +} +``` + +* 至于接口写在内部还是外部,具体可以看情况而定,如果功能比较庞大, + +#### 布局文件命名规范 + +* 以 `模块` + `类型` 来命名,例如 + + * `home_activity.xml` + + * `setting_fragment.xml` + + * `menu_item.xml` + + * `address_dialog.xml` + +* 这样写的好处在于,由于 res 文件夹下是没有层级概念的 + +* 通过前缀的命名可以帮助我们更好定位到同一模块下的资源 + +* 例如分享对话框中,有对话框 Root 布局和 Item 布局 + + * `share_dialog.xml`(Root 布局) + + * `share_item.xml`(Item 布局) + +#### 资源文件命名规范 + +* 如果是业务模块下的资源,以 `模块` + `类型` 来命名,例如分享对话框的资源: + + * `share_link_ic.png`(复制链接) + + * `share_moment_ic.png`(分享到朋友圈) + + * `share_qq_ic.png`(分享到 QQ 好友) + + * `share_qzone_ic.png`(分享到 QQ 空间) + + * `share_wechat_ic.png`(分享到微信好友) + +* 如果和业务模块不相干的资源,以 `作用` + `类型` 来命名,例如通用的控件样式资源: + + * `button_rect_selector.xml`(通用直角按钮样式) + + * `button_round_selector.xml`(通用圆角按钮样式) + +* 这种资源有一个共同特点,它不属于哪个模块,但是每个模块都有用到,所以不能用模块名作为文件名前缀 + +#### 接口实现规范 + +* 一般情况下,我们会在类中这样实现接口,这样写的好处是,可以减少对象的创建,并且代码也比较美观 + +```java +public final class PasswordEditText extends EditText + implements View.OnTouchListener, + View.OnFocusChangeListener, TextWatcher { + + public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOnTouchListener(this); + setOnFocusChangeListener(this); + addTextChangedListener(this); + } + + @Override + public void onFocusChange(View view, boolean hasFocus) { + ...... + } + + @Override + public boolean onTouch(View view, MotionEvent event) { + ...... + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + ...... + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void afterTextChanged(Editable s) {} +} +``` + +* 但是有一个美中不足的地方,就是在实现的接口过多时,我们很难分辨是哪个方法是哪个接口的,这个时候可以使用注释的方式来解决这个问题,加上 `@link` 还可以帮助我们快速定位接口类在项目中所在的位置 + +```java +public final class PasswordEditText extends EditText + implements View.OnTouchListener, + View.OnFocusChangeListener, TextWatcher { + + public PasswordEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOnTouchListener(this); + setOnFocusChangeListener(this); + addTextChangedListener(this); + } + + /** + * {@link OnFocusChangeListener} + */ + + @Override + public void onFocusChange(View view, boolean hasFocus) { + ...... + } + + /** + * {@link OnTouchListener} + */ + + @Override + public boolean onTouch(View view, MotionEvent event) { + ...... + } + + /** + * {@link TextWatcher} + */ + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + ...... + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void afterTextChanged(Editable s) {} +} +``` + +#### 异常捕获规范 + +* 请不要使用此方式捕获异常,因为这种方式会把问题给隐藏掉,会加大问题的排查难度 + +```java +try { + xxxxx.xxx(); +} catch (Exception e) {} +``` +* 如需捕获异常,请用以下方式进行捕获 + +```java +// 捕获这个异常,避免程序崩溃 +try { + // 目前发现在 Android 7.1 主线程被阻塞之后弹吐司会导致崩溃,可使用 Thread.sleep(5000) 进行复现 + // 查看源码得知 Google 已经在 Android 8.0 已经修复了此问题 + // 主线程阻塞之后 Toast 也会被阻塞,Toast 因为超时导致 Window Token 失效 + mHandler.handleMessage(msg); +} catch (WindowManager.BadTokenException | IllegalStateException ignored) { + // android.view.WindowManager$BadTokenException:Unable to add window -- token android.os.BinderProxy is not valid; is your activity running? + // java.lang.IllegalStateException:View android.widget.TextView has already been added to the window manager. + e.printStackTrace(); +} +``` + +* 必须要在 try 块中说明崩溃的缘由,并注明抛出的异常信息,并在代码中输出对应的日志 + +#### Activity 跳转约定 + +* 应当将 Intent 中的 key 常量保存到一个管理类中,又或者定在目标的 Activity 中 + +```java +public class IntentKey { + + /** id */ + public static final String ID = "id"; + /** token */ + public static final String TOKEN = "token"; + /** 订单 */ + public static final String ORDER = "order"; + /** 余额 */ + public static final String BALANCE = "balance"; + /** 时间 */ + public static final String TIME = "time"; + /** URL */ + public static final String URL = "url"; + /** 路径 */ + public static final String PATH = "path"; + /** 其他 */ + public static final String OTHER = "other"; + + ..... +} +``` + +* 如果跳转的 Activity 需要传递参数,应该在目标的 Activity 中定义静态的 `start` 又或者 `newIntent `方法 + +```java +public final class WebActivity extends Activity { + + public static void start(Context context, String url) { + Intent intent = new Intent(context, WebActivity.class); + intent.putExtra(IntentKey.URL, url); + context.startActivity(intent); + } +} +``` + +```java +public final class WebActivity extends Activity { + + public static Intent newIntent(Context context, String url) { + Intent intent = new Intent(context, WebActivity.class); + intent.putExtra(IntentKey.URL, url); + return intent; + } +} +``` + +* 如果创建的 Fragment 需要传递参数,应该在目标的 Fragment 中定义静态的 newInstance 方法 + +```java +public final class WebFragment extends Fragment { + + public static WebFragment newInstance(String url) { + WebFragment fragment = new WebFragment(); + Bundle bundle = new Bundle(); + bundle.putString(IntentKey.URL, url); + fragment.setArguments(bundle); + return fragment; + } +} +``` + +* 如果跳转的 Activity 或者创建的 Fragment 不需要传任何参数,可以不需要定义这些静态方法 + +#### 第三方框架使用规范 + +* 集成一些第三方框架或者 SDK,必须注明作用和出处,以便出现问题时能够快速核查和反馈 + +```groovy +// 图片加载框架:https://github.com/bumptech/glide +implementation 'com.github.bumptech.glide:glide:4.12.0' +annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0' +``` + +* 不能选择功能两套相同的框架,应当引用最合适的一套框架进行开发,由此来降低代码的耦合度 + +* 使用第三方库必须要依赖指定的版本号,而不能使用 + 号来指定依赖库最新的版本号 + +* 使用第三方开源库出现问题或者 Bug 时应及时通知到开源库的作者,如果没有及时回复就根据实际情况对问题进行修复 + +* 尽量避免 Copy 第三方库的技术代码到项目中,特别是在放置到项目业务模块中,因为这样会增加项目的复杂度,从而降低可维护性 + +* 如果出现问题不能找到开源库的作者,如果需要修改,应当将这些代码抽取到单独的 Module 中 + +* 能用框架就用成熟框架,尽量不要自己编写或者修改框架,如果有需要,要对这块进行严格测试 + +#### 代码注释规范 + +* 类注释规范:author 是创建者(必填项)、time 是创建时间(必填项)、desc 是类的描述(必填项),doc 是文档地址(非必填),github 是开源地址(如果项目是开源的则必填,否则不填) + +```java +/** + * author : 黄锦群 + * github : https://github.com/getActivity/XXPermissions + * time : 2018/06/15 + * desc : 权限请求实体类,参考 {@link Manifest.permission} + * doc : https://developer.android.google.cn/reference/android/Manifest.permission?hl=zh_cn + * https://developer.android.google.cn/guide/topics/permissions/overview?hl=zh-cn#normal-dangerous + */ +public final class Permission { + .... +} +``` + +* 方法注释规范:方法注释可根据实际情况而定,建议尽量用规范的命名来减少不必要的注释 + +```java +/** + * 设置请求的对象 + * + * @param activity 当前 Activity,可以传入栈顶的 Activity + */ +public static XXPermissions with(FragmentActivity activity) { + return ....; +} +``` + +* 字段注释规范:根据字段的作用而定,建议尽量用规范的命名来减少不必要的注释 + +```java +/** 请求的权限组 */ +private static final String REQUEST_PERMISSIONS = "request_permissions"; + +/** 权限回调对象 */ +private OnPermissionCallback mCallBack; +``` + +* 变量注释规范(如果 API 是比较常见并且容易理解可以不用写,如果是复杂并且羞涩难懂则需要写上) + +```java +// 设置保留实例,不会因为屏幕方向或配置变化而重新创建 +fragment.setRetainInstance(true); +``` + +#### String ID 命名规范 + +* 以 `模块` + `功能` 来命名,例如 + +```xml + +首页 +发现 +消息 +我的 +再按一次退出 + + +注册 +请输入手机号 +请输入密码 +忘记密码? +登录 +其他登录方式 + + +注册 +手机号仅用于登录和保护账号安全 +登录 +设置密码 +再次输入密码 +两次密码输入不一致,请重新输入 + + +设置 +语言切换 +简体中文 +繁体中文 +``` + +#### Anim ID 命名规范 + +* 应用到某个模块 View + +```xml +login_left_balloon_view.xml +login_right_balloon_view.xml +``` + +* 应用到全局 Activity + +```xml +left_in_activity.xml +left_out_activity.xml +``` + +* 应用到全局 Dialog + +```xml +bottom_in_dialog.xml +bottom_out_dialog.xml +``` + +#### View ID 命名规范 + +* 应该以`控件的缩写` + `模块名` + `作用` 来命名,例如 + +```xml +@+id/R.id.rg_login_type + +@+id/R.id.et_login_phone + +@+id/R.id.et_login_sms + +@+id/R.id.et_login_password + +@+id/R.id.btn_login_commit +``` + +* View 和 Layout 控件缩写表,这里列举最常见的几个 + +| 名称 | 缩写 | +| :-----: | :----: | +| TextView | tv | +| EditText | et | +| Button | btn | +| ImageView | iv | +| ImageButton | ib | +| ListView | lv | +| RecyclerView | rv | +| RadioButton | rb | +| RadioGroup | rg | +| ProgressBar | pb | +| CheckBox | cb | +| TableLayout | tl | +| ScrollView | sv | +| LinearLayout | ll | +| RelativeLayout | rl | +| FrameLayout | fl | + +#### Style 样式命名规范 + +* 如果只是主题相关的样式,以 `Theme` 命名结尾,否则以 `Style` 命名结尾,命名要求尽量简洁,并且需要有代码注释,示例如下: + +```xml + + + + + + + + +``` + +--- + +```xml + + + + + + + + + + + +``` + +#### 资源使用规范 + +* `Color` 和 `Dimens` 允许写死在代码中,但是如果某个色值引用得比较多(例如主题强调色、通用背景色),需要进行抽取和统一 + +* 对于一些常用并且样式比较统一的控件,例如 `Button`、`EditText` 等,我们对这些控件的样式进行抽取到 `style.xml` 中来 + +* String 建议不要写死在 Java 和 Xml 代码中,如果项目没有国际化的需求,可以考虑直接写死,如果项目后续可能有国际化的需求,则不能直接写死在布局文件中 + +#### XML 编码规范 + +* 不推荐用 `dp` 作为字体单位,虽然在大部分手机上面 `dp` 和 `sp` 计算是差不多的,但是会有一部分老年用户群,例如咱们的爸爸妈妈爷爷奶奶,他们通常会把手机显示的字体大小调大,这样他们才不需要带眼镜看手机,如果我们用 `dp` 作为字体单位,无论手机怎么调整字体大小,应用的字体大小都不会有任何的变化,所以这种操作显然是非常不人性化的。 + +```xml + + + + + +``` + +* 不能根据设计图给定的宽高把 `TextView` 或者 `Button` 的宽高定死,而是通过 `wrap_content` 的方式和 `padding` 来调整 View 的宽高,因为在不同手机上面字体大小不一致,在字体显示比较小的手机上面会显示正常,但是在字体显示比较大的平板上面文字上半部分极有可能会出现被裁剪的情况,所以我们不能把宽高定死,而是通过 `padding` 来调整到想要的控件大小。 + +```xml + +