前言:
阳春三月,万物复苏,三月的第一天,各大技术公众号就被阿里巴巴的android开放手册刷屏了。第一时间学习了阿里的开放规范后,发现之前自己公司的android项目,还是存在一些不规范的开发习惯,所以想在此总结一下开发手册里的编码以及开发要点,作为以后开发的准则,并贯彻执行。文中共分为八大模块(除去了java开发规范),我会分别进行总结归纳,并结合自己的项目,有选择性的总结出适合自己的一套开发规范,对于其中还没用到的知识要点,留到以后使用了再进行研究。
源文件地址:阿里巴巴Android开发手册
目的:
借用开发手册里开发规约的目标
- 防患未然,提升质量意识,降低故障率和维护成本;
- 标准统一,提升协作效率;
- 追求卓越的工匠精神,打磨精品代码。
全文总结:
一,Android资源文件命名与使用
1,资源文件需加上模块前缀。
2,layout文件命名:以模块前缀+布局属性
Activity的layout以module_activity开头
Fragment 的 layout 以 module_fragment 开头
Dialog 的 layout 以 module_dialog 开头
ListView 的行 layout 以 module_list_item 开头
RecyclerView 的 item layout 以 module_recycle_item 开头
3,drawable 资源名称以小写单词+下划线的方式命名,根据分辨率不同存放
在不同的 drawable 目录下,建议只使用一套,例如 drawable-xhdpi。
模块名_业务功能描述_控件描述_控件状态
如:module_login_btn_pressed,module_tabs_icon_home_normal
4,anim 资源名称以小写单词+下划线的方式命名,采用以下规则:
模块名_逻辑名称_[方向|序号]
tween 动画资源 : 尽可能以通用的动画名称命名,如 module_fade_in ,
module_fade_out , module_push_down_in (动画+方向);
frame 动画资源:尽可能以模 块+功能命名+序号。如:module_loading_grey_001
5,color 资源使用#AARRGGBB 格式,写入 module_colors.xml 文件中,命名格式采用以下规则:
模块名_逻辑名称_颜色
如:<color name="module_btn_bg_color">#33b5e5e5</color>
6,string资源文件或者文本用到字符需要全部写入module_strings.xml文件中,字符串以小写单词+下划线的方式命名,采用以下规则:
模块名_逻辑名称
如:moudule_login_tips,module_homepage_notice_desc
7,Id 资源原则上以驼峰法命名,View 组件的资源 id 需要以 View 的缩写作为前缀。常用缩写表如下:
8,大分辨率图片(单维度超过 1000)大分辨率图片建议统一放在 xxhdpi 目录下管理,否则将导致占用内存成倍数增加。
为适应android多屏幕适配,相应分辨率的资源文件应放在密度特定资源的配置限定符修饰的文件夹内。包括 ldpi(低)、mdpi(中)、 hdpi(高)、xhdpi(超高)、xxhdpi (超超高)和 xxxhdpi(超超超高)。
二,Android基本组件
Android 基本组件包括 Activity、Fragment、Service,BroadcastReceiver、ContentProvider 等等。
1,Activity 间的数据通信,对于数据量比较大的,避免使用 Intent + Parcelable的方式,可以考虑 EventBus 等替代方案,以免造成 TransactionTooLargeException。
2,Activity 间通过隐式 Intent 的跳转,在发出 Intent 之前必须通过 resolveActivity检查,避免找不到合适的调用组件,造成 ActivityNotFoundException 的异常。
3,避免在 Service#onStartCommand()/onBind()方法中执行耗时操作,如果确实有需求,应改用 IntentService 或采用其他异步机制完成。
4,避免在 BroadcastReceiver#onReceive()中执行耗时操作,如果有耗时工作,应该创建 IntentService 完成,而不应该在 BroadcastReceiver 内创建子线程去做。
5,避免使用隐式 Intent 广播敏感信息,信息可能被其他注册了对应
BroadcastReceiver 的 App 接收。
如果广播仅限于应用内,则可以使用 LocalBroadcastManager#sendBroadcast()实现,避免敏感信息外泄和 Intent 拦截的风险。
6,不要在 Activity#onDestroy()内执行释放资源的工作,例如一些工作线程的销毁和停止,因为 onDestroy()执行的时机可能较晚。可根据实际需要,在Activity#onPause()/onStop()中结合 isFinishing()的判断来执行。
7,Service 需要以多线程来并发处理多个启动请求,建议使用 IntentService,可避免各种复杂的设置。
8,对于只用于应用内的广播,优先使用 LocalBroadcastManager 来进行注册和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率。
9,当前Activity的onPause方法执行结束后才会执行下一个Activity的onCreate方法,所以在 onPause 方法中不适合做耗时较长的工作,这会影响到页面之间的跳转效率。
10,不要在 Android 的 Application 对象中缓存数据。基础组件之间的数据共享请使用 Intent 等机制,也可使用 SharedPreferences 等数据持久化机制。
11,使用 Adapter 的时候,如果你使用了 ViewHolder 做缓存,在 getView()的方法中无论这项 convertView 的每个子控件是否需要设置属性(比如某个 TextView设置的文本可能为 null,某个按钮的背景色为透明,某控件的颜色为透明等),都需要为其显式设置属性(Textview 的文本为空也需要设置 setText(""),背景透明也需要设置),否则在滑动的过程中,因为 adapter item 复用的原因,会出现内容的显示错乱。
12,Activity或者 Fragment 中动态注册BroadCastReceiver 时,registerReceiver()和 unregisterReceiver()要成对出现。
说明:
如果 registerReceiver()和 unregisterReceiver()不成对出现,则可能导致已经注册的receiver 没有在合适的时机注销,导致内存泄漏,占用内存空间,加重 SystemService负担。
部分华为的机型会对 receiver 进行资源管控,单个应用注册过多 receiver 会触发管控模块抛出异常,应用直接崩溃。
三,UI布局
1,布局中不得不使用 ViewGroup 多重嵌套时,尽量不要使用 LinearLayout 嵌套,改用 RelativeLayout,可以有效降低嵌套数。
2,在 Activity 中显示对话框或弹出浮层时,尽量使用 DialogFragment,而非Dialog/AlertDialog,这样便于随Activity生命周期管理对话框/弹出浮层的生命周期。
3,灵活使用布局,推荐 Merge、ViewStub 来优化布局,尽可能多的减少 UI布局层级,推荐使用 FrameLayout,LinearLayout、RelativeLayout 次之。
4,不能在 Activity 没有完全显示时显示 PopupWindow 和 Dialog。
5,尽量不要使用 AnimationDrawable,它在初始化的时候就将所有图片加载
到内存中,特别占内存,并且还不能释放,释放之后下次进入再次加载时会报错。
如果必须使用帧动画可以使用 AnimationDrawable 实现,但是如果你的帧动画中如果包含过多帧图片,一次性加载所有帧图片所导致的内存消耗会使低端机发生 OOM异常。帧动画所使用的图片要注意降低内存消耗,当图片比较大时,容易出现 OOM。
6,不能使用 ScrollView 包裹 ListView/GridView/ExpandableListVIew;因为这
样会把 ListView 的所有 Item 都加载到内存中,要消耗巨大的内存和 cpu 去绘制图面。目前为了较好的 UI 体验,更贴近 Material Design 的设计,推荐使用 NestedScrollView。
四,进程,线程与消息通信
1,不要通过 Intent 在 Android 基础组件之间传递大数据(binder transaction
缓存为 1MB),可能导致 OOM。
2,新建线程时,必须通过线程池提供(AsyncTask 或者 ThreadPoolExecutor或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。可以在项目中创建一个线程池管理,然后每次开启线程都从线程管理中去开启,合理利用资源。
3,子线程中不能更新界面,更新界面必须在主线程中进行,网络操作不能在
主线程中调用。
五,文件与数据库
1,任何时候不要硬编码文件路径,请使用 Android 文件系统 API 访问。
2,当使用外部存储时,必须检查外部存储的可用性。
3,SharedPreference 中只能存储简单数据类型(int、boolean、String 等),
复杂数据类型建议使用文件、数据库等其他方式存储。
4,SharedPreference 提 交 数 据 时 , 尽 量 使 用 Editor#apply() ,而非
Editor#commit()。一般来讲,仅当需要确定提交结果,并据此有后续操作时,才使用 Editor#commit()。
5,数据库 Cursor 必须确保使用完后关闭,以免内存泄漏。
6,多线程操作写入数据库时,需要使用事务,以免出现同步问题。
7,大数据写入数据库时,请使用事务或其他能够提高 I/O 效率的机制,保证执
行速度。
六,Bitmap,Drawable与动画
1,加载大图片或者一次性加载多张图片,应该在异步线程中进行。图片的加
载,涉及到 IO 操作,以及 CPU 密集操作,很可能引起卡顿。
2,在 ListView,ViewPager,RecyclerView,GirdView 等组件中使用图片时,
应做好图片的缓存,避免始终持有图片导致内存泄露,也避免重复创建图片,引起性 能 问 题 。 建 议 使 用 Fresco ( https://github.com/facebook/fresco )、 Glide(https://github.com/bumptech/glide)等图片库。
3,png 图片使用 tinypng 或者类似工具压缩处理,减少包体积
4,应根据实际展示需要,压缩图片,而不是直接显示原图。手机屏幕比较小,
直接显示原图,并不会增加视觉上的收益,但是却会耗费大量宝贵的内存。
5,使用完毕的图片,应该及时回收,释放宝贵的内存。
6,在 Activity.onPause()或 Activity.onStop()回调中,关闭当前 activity 正在执
行的的动画。
7,当 View Animation 执行结束时,调用 View.clearAnimation()释放相关资源。
七,安全
1,使用 PendingIntent 时,禁止使用空 intent,同时禁止使用隐式 Intent
说明:
- 使用 PendingIntent 时,使用了空 Intent,会导致恶意用户劫持修改 Intent 的内
容。禁止使用一个空 Intent 去构造 PendingIntent,构造 PendingIntent 的 Intent
一定要设置 ComponentName 或者 action。 - PendingIntent 可以让其他 APP 中的代码像是运行自己 APP 中。PendingIntent
的intent接收方在使用该intent时与发送方有相同的权限。在使用PendingIntent
时,PendingIntent 中包装的 intent 如果是隐式的 Intent,容易遭到劫持,导致
信息泄露。
2,将 android:allowbackup 属性设置为 false,防止 adb backup 导出数据。
说明:
在 AndroidManifest.xml 文件中为了方便对程序数据的备份和恢复在 Android API level 8 以后增加了 android:allowBackup 属性值。默认情况下这个属性值为 true,故当 allowBackup 标志值为 true 时,即可通过 adb backup 和 adb restore 来备份和恢复应用程序数据。
3,不要把敏感信息打印到 log 中。在产品的线上版本中关闭调试接口,不要输出敏感信息。
4,对于内部使用的组件,显示设置组件的"android:exported"属性为 false。
5,除非 min API level >=17,请注意 addJavascriptInterface 的使用。
6,开放的 activity/service/receiver 等需要对传入的 intent 做合法性校验。
八,其他
1,不要通过 Msg 传递大的对象,会导致内存问题。
2,Log 的 tag 不能是" "。
小结:
文中总结内容基本上是根据《阿里巴巴android开发手册》上来的,只是省去了一些示例代码。其中资源命名部分是提高代码清晰度的一个很重要环节,统一的命名规则能快速定位资源以及理解其在程序中的用途。其他几类主要是优化编程习惯以及提高代码质量的规范,只有在实际编程过程中去践行这些规则,才能真实的提高代码质量,提升代码的可读性。