注释
类/复杂或者不能从方法名字看出意图的方法必须添加注释
/**
* @Author: LiuJinYang
* @CreateDate: 2020/5/16 9:32
* 网络请求工具类
*/
public class RetrofitUtil {
/**
* 添加公参basic
*
* @param key 公参字段名
* @param value 公参值
*/
public void addBasic(String key, String value) {
basicMap.put(key, value);
}
}
命名
类命名,类文件名使用 UpperCameCase 风格, 必须遵从驼峰形式, 且要保证从类的名称上能区分出它是什么功能类型;
方法名第一个单词要体现出方法的功能 例如:save, set, get, insert, create;
对象使用小驼峰方式, 例: LoginPresenter loginPresenter, NewsBean newsBean;
全局变量和局部变量功能相同时:全局加 “m”。参数名,可以和成员变量名相同,但要注意引用成员变量时,需要加上 this 字段
常量:全部大写,单词之间使用 “_” 分隔。
代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文方式;正确的英文拼写和语法可以让阅读者易于理解, 避免歧义。纯拼音命名方式也不要使用。
抽象类命名使用 Abstract 或者 Base 开头,异常类命名使用 Exception 结尾;测试类以他要测试的类的名称开始,以 Test 结束。
资源文件命名
layout文件命名:
Activity 的 layout 以 module_activity 开头
Fragment 的 layout 以 module_fragment 开头
Dialog 的 layout 以 module_dialog 开头
include 的 layout 以 module_include 开头
ListView 的行 layout 以 module_list_item 开头
RecyclerView 的 item layout 以 module_recycle_item 开头
GridView 的 item layout 以 module_grid_item 开头
drawable命名:
模块名_业务功能描述_控件描述_控件状态限定词, 如:module_login_btn_pressed,module_tabs_icon_home_normal
anim 命名:
模块名_逻辑名称_[方向|序号],如 module_fade_in , module_fade_out , module_push_down_in
color命名:
模块名_逻辑名称_颜色,如:#33b5e5e5
dimen 命名:
模块名_描述信息,如:1dp
string 命名:
模块名_逻辑名称,如:moudule_login_tips,module_homepage_notice_desc
id 命名:
推荐使用小写字母并用下划线进行分割,View 组件的资源 id 建议以 View 的缩写作为前缀。常用缩写表如下:
LinearLayout ll
RelativeLayout rl
ConstraintLayout cl
ListView lv
ScollView sv
TextView tv
Button btn
ImageView iv
CheckBox cb
RadioButton rb
EditText et
Android 基本组件
Android 基 本 组 件 指 Activity 、 Fragment 、 Service 、 BroadcastReceiver 、ContentProvider 等等。
Activity 间的数据通信, 对于数据量比较大的,避免使用 Intent + Parcelable的方式,可以考虑 EventBus/RxBus 等替代方案,以免造成 TransactionTooLargeException。
Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。
避免在 Service#onStartCommand()/onBind()方法中执行耗时操作,如果确 实有需求,应改用 IntentService 或采用其他异步机制完成。
避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作, 应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。
避免使用隐式 Intent 广播敏感信息,信息可能被其他注册了对应 BroadcastReceiver 的 App 接收。
对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册 和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
Activity或者Fragment中动态注册BroadCastReceiver时,registerReceiver() 和 unregisterReceiver()要成对出现。
不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的 销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在Activity的onPause()/onStop()中结合 isFinishing()的判断来执行。
当前 Activity 的 onPause 方法执行结束后才会创建(onCreate)或恢复 (onRestart)别的 Activity,所以在 onPause方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
Service 需要以多线程来并发处理多个启动请求,建议使用 IntentService, 可避免各种复杂的设置。
UI 与布局
布局xml优先使用ConstraintLayout, 可以保证无嵌套的情况下完成包括部分控件同时显隐需求在内的99%的布局要求;
布局中不得不使用 ViewGroup 多重嵌套时,不要使用 LinearLayout 嵌套, 改用 RelativeLayout,可以有效降低嵌套数。
在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非 Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期。
禁止在非 UI 线程进行 View 相关操作。
禁止在设计布局时多次为子 View 和父 View 设置同样背景进而造成页面过 度绘制,推荐将不需要显示的布局进行及时隐藏。
不能使用 ScrollView 包裹 RecyclerView/ListView/GridView/ExpandableListVIew;因为这 样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制图面。
不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享 请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制。
使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的 方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView 设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需 要为其显式设置属性(Textview 的文本为空也需要设置 setText(""),背景透明也需要 设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错 乱。
进程、线程与消息通信
不要通过 Intent 在 Android 基础组件之间传递大数据(binder transaction 缓存为 1MB),可能导致 OOM。
在 Application 的业务初始化代码加入进程判断,确保只在自己需要的进程 初始化。特别是后台进程减少不必要的业务初始化。
public class MyApplication extends Application {
@Override
public void onCreate() {
//在所有进程中初始化
....
//仅在主进程中初始化
if (mainProcess) {
...
}
//仅在后台进程中初始化
if (bgProcess) {
...
}
}
}
新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor 或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方 式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在 主线程中调用。
新建线程时,定义能识别自己业务的线程名称,便于性能优化和问题排查。
public class MyThread extends Thread {
public MyThread(){
super.setName("ThreadName");
…
}
}
ThreadPoolExecutor 设置线程存活时间(setKeepAliveTime),确保空闲时 线程能被释放。
禁 止 在 多 进 程 之 间 用 SharedPreferences 共 享 数 据 , 虽 然 可 以(MODE_MULTI_PROCESS,已过时),但官方已不推荐。
谨慎使用 Android 的多进程,多进程虽然能够降低主进程的内存压力,但 会遇到如下问题:
首次进入新启动进程的页面时会有延时的现象(有可能黑屏、白屏几秒,是白屏还是黑屏和新 Activity 的主题有关);
应用内多进程时,Application实例化多次,需要考虑各个模块是否都需要在所有进程中初始化。
文件与数据库
任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。
当使用外部存储时,必须检查外部存储的可用性。
应用间共享文件时,不要通过放宽文件系统权限的方式去实现,而应使用 FileProvider。
SharedPreference 中只能存储简单数据类型(int、boolean、String 等), 复杂数据类型建议使用文件、数据库等其他方式存储。
数据库 Cursor 必须确保使用完后关闭,以免内存泄漏。
多线程操作写入数据库时,需要使用事务,以免出现同步问题。
执行 SQL 语句时,应使用 SQLiteDatabase#insert()、update()、delete(), 不要使用 SQLiteDatabase#execSQL(),以免 SQL 注入风险。
如果 ContentProvider 管理的数据存储在 SQL 数据库中,应该避免将不受 信任的外部数据直接拼接在原始 SQL 语句中。
Bitmap、Drawable 与动画
png 图片使用 TinyPNG 或者类似工具压缩处理,减少包体积。
使用完毕的图片,应该及时回收,释放宝贵的内存。
Bitmap bitmap = null;
loadBitmapAsync(new OnResult(result){
bitmap = result;
});
...使用该 bitmap...// 使用结束,在 2.3.3 及以下需要调用 recycle()函数,在 2.3.3 以上 GC 会自动管理,除非你明确不需要再用。
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
bitmap.recycle();}
bitmap = null;
在 Activity#onPause()或 Activity#onStop()回调中,关闭当前 activity 正在执行的的动画。
public void onPause() {
//页面退出,及时清理动画资源
mImageView.clearAnimation()
}
在动画或者其他异步任务结束时,应该考虑回调时刻的环境是否还支持业 务处理。例如 Activity 的onStop() 函数已经执行, 且在该函数中主动释放了资源, 此时回调中如果不做判断就会空指针崩溃。
使用 RGB_565 代替 RGB_888,在不怎么降低视觉效果的前提下,减少内 存占用。
大图片资源不要直接打包到 apk,可以考虑通过文件仓库远程下载,减小包 体积。
当 View Animation 执行结束时,调用View.clearAnimation()释放相关资源。
安全
将 android:allowbackup 属性必须设置为 false,阻止应用数据被导出。
如果使用自定义 HostnameVerifier 实现类,必须在 verify()方法中校验服务 器主机名的合法性,否则可能受到中间人攻击;如果使用自定义X509TrustManager 实现类,必须在 checkServerTrusted()方法中校验服务端证书的合法性,否则可能受到中间人攻击。
HostnameVerifier hnv = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if("yourhostname".equals(hostname)){
return true;
} else {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return hv.verify(hostname, session);
}
}
};
在 SDK 支持的情况下,Android 应用必须使用 V2 签名,这将对 APK 文 件的修改做更多的保护。
所有的 Android 基本组件(Activity、Service、BroadcastReceiver、 ContentProvider 等)都不应在没有严格权限控制的情况下,将android:exported 设置为 true。
WebView 应设置 WebView#getSettings()#setAllowFileAccess(false)、 WebView#getSettings()#setAllowFileAccessFromFileURLs(false) 、 WebView#getSettings()#setAllowUniversalAccessFromFileURLs(false),阻止 file scheme URL 的访问。
不要把敏感信息打印到 log 中。
确保应用发布版本的 android:debuggable 属性设置为 false。
本地加密秘钥不能硬编码在代码中,更不能使用 SharedPreferences 等本 地持久化机制存储。应选择 Android自身的秘钥库(KeyStore)机制或者其他安全 性更高的安全解决方案保存。
addJavascriptInterface() 可以添加 JS 对本地 Java 方法的调用,但这本身 会导致恶意代码的攻击。在 Android 4.2(API Level 17)以下, 不应再使用这样的调用方式。在 Android 4.2 及以上, 需要对本地被远程调用的方法显式添加@JavascriptInterface annotation。
使用 Android 的 AES/DES/DESede 加密算法时,不要使用 ECB 加密模式, 应使用 CBC 或 CFB 加密模式; MD5 和 SHA-1、SHA-256 等常用算法是 Hash 算法,有一定的安全性,但不能代替加密算法。敏感信息的存储和传输,需要使用专业的加密机制。
加密模式有 ECB、CBC、CFB、OFB 等,其中 ECB 的安全性较弱,如果使用固
定的密钥,相同的明文将会生成相同的密文,容易受到字典攻击,建议使用 CBC、
CFB 或 OFB 等模式。
1) ECB:Electronic codebook,电子密码本模式
2) CBC:Cipher-block chaining,密码分组链接模式
3) CFB:Cipher feedback,密文反馈模式
4) OFB:Output feedback,输出反馈模式
Android APP 在 HTTPS 通信中,验证策略需要改成严格模式。
在 Android 4.2(API Level 17)及以上,对安全性要求较高的应用可在 Activity中,对 Activity 所关联的 Window 应用 WindowManager.LayoutParams.FLAG_SECURE, 防止被截屏、录屏。但要注意的是,一个 Activity 关联的 Window 可能不止一个,如果使用了 Dialog / DialogFragment 等控件弹出对话框,它们本身也会创建一个新的 Window,也一样需要保护。
系统设计
不允许出现两段相同的逻辑块, 必须抽出为公共方法, 差异性使用参数控制, 避免修改时多处修改导致遗漏;
不允许出现两段相同的处于同一逻辑组的复杂布局, 必须抽为单独的include/merge;
不允许Activity内多Fragment之间的直接沟通, 必须通过Activity中转;
采用模块分类方式替代文件类别方式, 方便快速查找模块相关内容, 例: LoginActivity/LoginPreenter/LoginHttpRequest/LoginBean/LoginAdapter等所属同一登录模块的文件放入一个文件夹, 而不是所有activity放入一个文件夹, 所有adapter放入一个文件夹。
所有新定义的类/方法, 默认写成private, 只有在其他类需要引用时再看情况标为public, protected, package-private;
java定义的父类中定义的方法如果子类重写会导致问题时, 添加final关键字;
其他
不能使用 System.out.println 打印 log。
Log 的 tag 不能是" "。
项目建立REWADME.md文件,书写版本迭代中较重要的修改,包括需求修改,代码修改,三方库引用等;
参考资料
阿里巴巴 Android 开发手册