移动Android
规范文档
背景:
为了统一成都xxx有限公司移动部门的开发规范,特出此文,勿盲目借鉴.
本手册以开发者为中心视角分为 Android
资源文件命名与使用,Android
基本组件,UI
与布局,进程、线程与消息通信, 文件与数据库,Bitmap
、Drawable
与动画,安全,其他等九大部分,根据约束力强弱, 规约依次分为强制、推荐、参考三大类:
- 【
强制
】必须遵守,违反本约定或将会引起严重的后果; - 【
推荐
】尽量遵守,长期遵守有助于系统稳定性和合作效率的提升; - 【
参考
】充分理解,技术意识的引导,是个人学习、团队沟通、项目合作的方 向。
目标是
- 防患未然,提升质量意识,降低故障率和维护成本;
- 标准统一,提升协作效率;
- 追求卓越的工匠精神,打磨精品代码。
注释
【强制
】类注释
每个类必须严格意义上按照功能注释使用
推荐规则:
/**
*Describe:朝拜时间闹钟界面
*
*Created by zhigang wei
*on 2018/4/18
*
*Company :cpx
*/
【强制
】方法注释
不是每个一方法都需要注释,但是核心方法必须要注释
/**
* 设置数据
*
* @param object 设置的数据源
*/
public void setData(Object... object) {
}
【推荐
】属性注释
关键属性需要注释,推荐使用第一种
/*倒计时-同时也是判断是否是第一次进入*/
private var clockS: Long = 0x111L
或者
/**
* 倒计时-同时也是判断是否是第一次进入
*/
private var clockS: Long = 0x111L
【推荐
】功能结构注释
如果一个功能类过于复杂,前期在满足开发任务的前提下需要记录后续优化的注释,后续需及时优化这块逻辑
/*start------------------倒计时---------------------------*/
xxx
xxx
xxx
/*end------------------倒计时---------------------------*/
【强制
】README
文档注释
每一个独立的module
都需要有各自的README
文档,介绍项目架构,逻辑处理,核心算法,每一次版本更新到具体内容等
Android
资源文件命名与使用
1. 【强制
】资源文件需带模块前缀解耦。
2. 【强制
】layout
文件的命名方式。
Activity
的layout
以activity_
开头Fragment
的layout
以fragment_
开头Dialog
的layout
以dialog_
开头include
的layout
以include_
开头- 自定义
view
的layout
以view_
开头 ListView
的行layout
以list_item
开头RecyclerView
的item layout
以recycle_item
开头 ;head
以recycle_item_head_
开头,footer
以recycle_item_footer_
开头GridView
的行layout
以grid_item
开头
3.【推荐
】Id 资源原则上以驼峰法命名,View 组件的资源 id 需要以 View 的缩写作为 前缀。常用缩写表如下
控件 | 缩写 |
---|---|
LinearLayout | ll |
RelativeLayout | rl |
ConstraintLayout | cl |
ScollView | sv |
RecyclerView | rv |
Button | btn |
ImageView | iv |
CheckBox | cb |
RadioButton | rb |
EditText | et |
TextView | tv |
类推其它控件的缩写推荐使用小写字母并用下划线进行分割
但是kotlin
使用不需要findview
所以也可以采用驼峰命名,个人推荐还是下划线来区分xml
4.【强制
】 color 资源使用#AARRGGBB 格式,写入baselib 中colors.xml 文件中
<color name="c_7F000000">#7F000000</color>
5.[强制
] 资源文件
-
自定义点击
selector_
,自定义点击文字selector_tv
,自定义图形shape_
统一放入都drawable
中 -
点九图都统一放入
drawable-xxhdpi
中 -
图片分别放入
mipmap
对应的资源中 -
其他大文件资源统一放入
assets
对应类型的文件夹中:例如
Android 基本组件
Android 基本组件指 Activity
、Fragment
、Service
、BroadcastReceiver
、ContentProvider
等等。
【推荐
】2018/11/6 补1.constraint-layout
项目中需要定义xml布局文件,根部局推荐使用ConstraintLayout
推荐原因请查看链接
1. 【强制
】Activity
间的数据通信,对于数据量比较大的,避免使用 Intent
+ Parcelable
的方式,可以考虑 RxBus
等替代方案,以免造成 TransactionTooLargeException
。
2. 【推荐
】Activity#onSaveInstanceState()
方法不是 Activity
生命周期方法,也不保证 一定会被调用。它是用来在 Activity
被意外销毁时保存 UI
状态的,只能用于保存临 时性数据,例如 UI
控件的属性等,不能跟数据的持久化存储混为一谈。持久化存储 应该在 Activity#onPause()/onStop()
中实行。
3.【强制
】Activity
间通过隐式 Intent
的跳转,在发出 Intent
之前必须通过 resolveActivity
检查,避免找不到合适的调用组件,造成 ActivityNotFoundException
的异常。
正例:
public void viewUrl(String url, String mimeType)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimeType);
if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ ONLY) != null) {
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
if (Config.LOGD) {
Log.d(LOGTAG, "activity not found for " + mimeType + " over " + Uri.parse(url).getScheme(), e);
}
}
}
}
反例:
Intent intent = new Intent();
intent.setAction("com.great.activity_intent.Intent_Demo1_Result3");
4.【强制
】避免在Service#onStartCommand()/onBind()
方法中执行耗时操作,如果确 实有需求,应改用IntentService
或采用其他异步机制完成。
5.【强制
】避免在 BroadcastReceiver#onReceive()
中执行耗时操作,如果有耗时工作, 应该创建IntentService
完成,而不应该在 BroadcastReceiver
内创建子线程去做。
说明:
由于该方法是在主线程执行,如果执行耗时操作会导致 UI
不流畅。可以使用 IntentService
、 创 建 HandlerThread
或 者 调 用Context#registerReceiver (BroadcastReceiver, IntentFilter, String, Handler)
方法等方式,在其他 Wroker
线程执行onReceive
方法。BroadcastReceiver#onReceive()
方法耗时超过 10 秒钟,可能会被系统杀死
6.【强制
】避免使用隐式Intent
广播敏感信息,信息可能被其他注册了对应 BroadcastReceiver
的 App
接收。
说明:
通过 Context#sendBroadcast()
发送的隐式广播会被所有感兴趣的 receiver
接收,恶意应用注册监听该广播的receiver
可能会获取到Intent
中传递的敏感信息,并进行 其他危险操作。如果发送的广播为使Context#sendOrderedBroadcast()
方法发送
的有序广播,优先级较高的恶意 receiver 可能直接丢弃该广播,造成服务不可用,或者向广播结果塞入恶意数据。如果广播仅限于应用内,则可以使用 LocalBroadcastManager#sendBroadcast()
实现,避免敏感信息外泄和Intent
拦截的风险
正例:
Intent intent = new Intent("my-sensitive-event");
intent.putExtra("event", "this is a test event");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
反例:
Intent intent = new Intent();
v1.setAction("com.sample.action.server_running");
v1.putExtra("local_ip", v0.h);
context.sendBroadcast(v1);
7. 【推荐
】 添 加 Fragment
时 , 确 保 FragmentTransaction#commit()
在 Activity#onPostResume()
或者 FragmentActivity#onResumeFragments()
内调用。 不要随意使用 FragmentTransaction#commitAllowingStateLoss()
来代替,任何 commitAllowingStateLoss()
的使用必须经过 code review
,确保无负面影响。
说明:
Activity
可能因为各种原因被销毁,Android
支持页面被销毁前通过 Activity#onSaveInstanceState()
保 存 自 己 的 状 态 。 但 如 果
FragmentTransaction.commit()
发生在 Activity
状态保存之后,就会导致 Activity
重 建、恢复状态时无法还原页面状态,从而可能出错。为了避免给用户造成不好的体验,系统会抛出 IllegalStateExceptionStateLoss
异常。推荐的做法是在Activity 的onPostResume()
或onResumeFragments()
对 FragmentActivity
里 执 行 FragmentTransaction.commit()
,如有必要也可在 onCreate()
里执行。不要随意改用FragmentTransaction.commitAllowingStateLoss()
或者直接使用 try-catch
避免 crash
,这不是问题的根本解决之道,当且仅当你确认Activity
重建、恢复状态时,本次 commit
丢失不会造成影响时才可这么做。
8. 【推荐
】不要在 Activity#onDestroy()
内执行释放资源的工作,例如一些工作线程的 销毁和停止,因为 onDestroy()
执行的时机可能较晚。可根据实际需要,在 Activity#onPause()/onStop()
中结合isFinishing()
的判断来执行。
9. 【推荐
】如非必须,避免使用嵌套的 Fragment
。可用自定义view
替代
说明:
嵌套 Fragment 是在 Android API 17 添加到 SDK 以及 Support 库中的功能,
Fragment 嵌套使用会有一些坑,容易出现 bug,比较常见的问题有如下几种:
- onActivityResult()方法的处理错乱,内嵌的 Fragment 可能收不到该方法的回调,需要由宿主 Fragment 进行转发处理;
- 突变动画效果;
- 被继承的 setRetainInstance(),导致在 Fragment 重建时多次触发不必要的逻辑。
10【推荐
】总是使用显式Intent启动或者绑定Service,且不要为服务声明IntentFilter, 保证应用的安全性。如果确实需要使用隐式调用,则可为 Service 提供 Intent Filter 并从 Intent 中排除相应的组件名称,但必须搭配使用 Intent#setPackage()方法设置 Intent 的指定包名,这样可以充分消除目标服务的不确定性。
11.【推荐
】Service 需要以多线程来并发处理多个启动请求,建议使用 IntentService, 可避免各种复杂的设置。
12.【推荐
】对于只用于应用内的广播,优先使用LocalBroadcastManager 来进行注册 和发送,LocalBroadcastManager 安全性更好,同时拥有更高的运行效率
正例:
对于使用 Context#sendBroadcast()等方法发送全局广播的代码进行提示。如果该广播仅用于应用内,则可以使用 LocalBroadcastManager 来避免广播泄漏以及广播被拦截等安全问题,同时相对全局广播本地广播的更高效。