# 新大洲 Android 开发规范 > 本规范基于阿里巴巴《Java开发手册》和 Android 开发最佳实践制定 ## 目录 - [一、编程规约](#一编程规约) - [1.1 命名风格](#11-命名风格) - [1.2 常量定义](#12-常量定义) - [1.3 代码格式](#13-代码格式) - [1.4 OOP规约](#14-oop规约) - [1.5 集合处理](#15-集合处理) - [1.6 控制语句](#16-控制语句) - [1.7 注释规约](#17-注释规约) - [二、异常日志](#二异常日志) - [2.1 异常处理](#21-异常处理) - [2.2 日志规约](#22-日志规约) - [三、安全规约](#三安全规约) - [四、Android 专项规范](#四android-专项规范) - [五、Kotlin 专项规范](#五kotlin-专项规范) --- ## 一、编程规约 ### 1.1 命名风格 #### 【强制】类名使用 UpperCamelCase 风格 **正例:** ```kotlin class LoginViewModel class AuthRepository class UserFragment ``` **反例:** ```kotlin class loginViewModel // ❌ 首字母小写 class Auth_Repository // ❌ 使用下划线 ``` #### 【强制】方法名、参数名、成员变量、局部变量统一使用 lowerCamelCase 风格 **正例:** ```kotlin fun login(mobile: String, password: String) private val loginState: StateFlow val userRepository: UserRepository ``` **反例:** ```kotlin fun Login() // ❌ 首字母大写 fun get_user_info() // ❌ 使用下划线 ``` #### 【强制】常量命名全部大写,单词间用下划线隔开 **正例:** ```kotlin companion object { const val MAX_PASSWORD_LENGTH = 16 const val MIN_PASSWORD_LENGTH = 6 const val SUCCESS_CODE = 0 } ``` **反例:** ```kotlin const val maxPasswordLength = 16 // ❌ 应该大写 const val SUCCESS_CODE = 0 // ✅ 正确 const val SuccessCode = 0 // ❌ 应该全大写+下划线 ``` #### 【强制】抽象类命名使用 Abstract 或 Base 开头 **正例:** ```kotlin abstract class BaseViewModel abstract class BaseRepository abstract class BaseFragment ``` ### 1.2 常量定义 #### 【强制】不允许任何魔法值(即未经定义的常量)直接出现在代码中 **反例:** ```kotlin // ❌ 错误示例 if (commonResult.code != 0) { // 魔法数字 0 // ... } if (password.length < 4 || password.length > 16) { // 魔法数字 4, 16 // ... } ``` **正例:** ```kotlin // ✅ 正确示例 object ApiConstants { /** 成功状态码 */ const val SUCCESS_CODE = 0 } object ValidationConstants { /** 密码最小长度 */ const val MIN_PASSWORD_LENGTH = 6 /** 密码最大长度 */ const val MAX_PASSWORD_LENGTH = 16 } // 使用 if (commonResult.code != ApiConstants.SUCCESS_CODE) { // ... } if (password.length < ValidationConstants.MIN_PASSWORD_LENGTH || password.length > ValidationConstants.MAX_PASSWORD_LENGTH) { // ... } ``` #### 【推荐】常量应该按功能分组,定义在对应的 Constants 类中 **目录结构:** ``` module/ ├── data/ │ └── constant/ │ └── ApiConstants.kt # API 相关常量 ├── domain/ │ └── constant/ │ └── ValidationConstants.kt # 验证相关常量 └── ui/ └── constant/ └── UiConstants.kt # UI 相关常量 ``` **示例:** ```kotlin // user/data/constant/ApiConstants.kt package com.narutohuo.xindazhou.user.data.constant /** * API 相关常量 */ object ApiConstants { /** 成功状态码 */ const val SUCCESS_CODE = 0 /** 请求超时时间(秒) */ const val TIMEOUT_SECONDS = 30 } // user/ui/constant/UiConstants.kt package com.narutohuo.xindazhou.user.ui.constant /** * UI 相关常量 */ object UiConstants { /** 提示信息 */ const val MSG_MOBILE_EMPTY = "请输入手机号" const val MSG_PASSWORD_EMPTY = "请输入密码" const val MSG_LOGIN_SUCCESS = "登录成功" const val MSG_LOGIN_FAILED = "登录失败" } ``` ### 1.3 代码格式 #### 【强制】大括号的使用约定 **正例:** ```kotlin // 如果大括号内为空,则简洁地写成 {} 即可 fun emptyFunction() {} // 如果大括号内非空,则: // 1) 左大括号前不换行 // 2) 左大括号后换行 // 3) 右大括号前换行 // 4) 右大括号后还有 else 等代码则不换行 if (condition) { statements } else { statements } ``` #### 【强制】单行字符数限制不超过 120 个 超出需要换行,换行时遵循以下原则: - 第二行相对第一行缩进 4 个空格 - 运算符与下文一起换行 - 方法调用的点符号与下文一起换行 **正例:** ```kotlin val result = repository.getUserInfo(mobile = mobile, password = password, deviceId = deviceId) val longString = "这是一个很长的字符串" + "需要换行显示" ``` ### 1.4 OOP规约 #### 【强制】避免通过类的实例对象访问静态成员 **正例:** ```kotlin // 通过类名访问 ApiConstants.SUCCESS_CODE ValidationConstants.MIN_PASSWORD_LENGTH ``` **反例:** ```kotlin // ❌ 通过实例访问(虽然Kotlin不推荐这样写) val constants = ApiConstants() constants.SUCCESS_CODE ``` #### 【推荐】类的成员方法之间、成员变量与成员方法之间,顺序建议按如下顺序排列 1. 伴生对象(companion object) 2. 常量定义(const val) 3. 成员变量(属性) 4. 初始化方法(init) 5. 公共方法 6. 私有方法 **示例:** ```kotlin class LoginViewModel(application: Application) : AndroidViewModel(application) { companion object { private const val TAG = "LoginViewModel" } private val authRepository = AuthRepository() private val _loginState = MutableStateFlow(LoginState.Idle) val loginState: StateFlow = _loginState fun login(mobile: String, password: String) { // ... } private fun validateInput(mobile: String, password: String): Boolean { // ... } } ``` ### 1.5 集合处理 #### 【强制】关于 hashCode 和 equals 的处理,遵循如下规则 - 只要重写 equals,就必须重写 hashCode - 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断 - Map 的 key 也是依据 hashCode 和 equals 进行判断 **正例:** ```kotlin data class User(val id: Long, val name: String) { // data class 会自动生成 equals 和 hashCode } ``` ### 1.6 控制语句 #### 【强制】当 switch 语句内的 case 分支数目超过 3 个时,必须使用 when 表达式 **正例:** ```kotlin when (state) { is LoginState.Idle -> { /* ... */ } is LoginState.Loading -> { /* ... */ } is LoginState.Success -> { /* ... */ } is LoginState.Error -> { /* ... */ } } ``` #### 【强制】在高并发场景中,避免使用"等于"判断作为中断或退出的条件 **说明:** 如果并发控制没有处理好,容易产生等值判断被"击穿"的情况,使用大于或小于的区间判断条件来代替。 ### 1.7 注释规约 #### 【强制】类、类属性、类方法的注释必须使用 Kotlin 文档注释(/** */) **正例:** ```kotlin /** * 认证数据仓库 * * 负责统一管理认证相关的数据获取和缓存 * * @author YourName * @date 2024-01-01 */ class AuthRepository { /** * 用户登录 * * @param mobile 手机号(11位数字) * @param password 密码(6-16位字符) * @return Result 登录结果,成功包含登录响应数据 */ suspend fun login(mobile: String, password: String): Result { // ... } } ``` #### 【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释 除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 #### 【推荐】代码修改的同时,注释也要进行相应的修改,特别是参数、返回值、异常、核心逻辑等的修改 --- ## 二、异常日志 ### 2.1 异常处理 #### 【强制】禁止使用 printStackTrace() 方法打印异常堆栈 **反例:** ```kotlin catch (e: Exception) { e.printStackTrace() // ❌ 禁止使用 } ``` **正例:** ```kotlin catch (e: Exception) { Timber.e(e, "$TAG - login: 登录异常") // ✅ 使用日志框架 } ``` #### 【强制】异常不要被生吞 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之。 **反例:** ```kotlin try { // ... } catch (e: Exception) { // ❌ 空 catch 块 } ``` **正例:** ```kotlin try { // ... } catch (e: Exception) { Timber.e(e, "$TAG - login: 登录异常") // 记录日志或返回错误结果 return Result.failure(e) } ``` #### 【强制】不要在 finally 块中使用 return #### 【推荐】方法的返回值可以为 null,不强制返回空集合(emptyList)或者空对象等 #### 【推荐】防止 NPE,是程序员的基本修养 **Kotlin 安全调用操作符:** **反例:** ```kotlin val commonResult = response.body()!! // ❌ 危险的非空断言 ``` **正例:** ```kotlin val commonResult = response.body() ?: return Result.failure(Exception("响应体为空")) // 或者 response.body()?.let { commonResult -> // 处理逻辑 } ?: run { return Result.failure(Exception("响应体为空")) } ``` ### 2.2 日志规约 #### 【强制】日志框架统一使用 Timber **正例:** ```kotlin import timber.log.Timber class AuthRepository { companion object { private const val TAG = "AuthRepository" } suspend fun login(mobile: String, password: String) { Timber.d("$TAG - login: 开始登录,mobile=${mobile.take(3)}***") // ... Timber.d("$TAG - login: 登录成功") } } ``` #### 【强制】日志输出必须使用占位符的方式 **反例:** ```kotlin // ❌ 错误:字符串拼接 Timber.d("$TAG - login: mobile=$mobile, password=$password") ``` **正例:** ```kotlin // ✅ 正确:Kotlin 的字符串模板(推荐) Timber.d("$TAG - login: mobile=${mobile.take(3)}***, passwordLength=${password.length}") // ✅ 或者使用 Timber 的格式化(推荐用于敏感信息) Timber.d("$TAG - login: 开始登录") ``` #### 【强制】日志级别说明 - **DEBUG**:调试信息,用于开发调试 - **INFO**:一般信息,记录程序正常运行的关键信息 - **WARN**:警告信息,表示可能存在潜在问题 - **ERROR**:错误信息,表示出现错误但不影响程序继续运行 - **ASSERT**:断言错误,表示严重的错误 **使用建议:** ```kotlin Timber.d("$TAG - method: 调试信息") // 开发调试 Timber.i("$TAG - method: 重要信息") // 关键业务节点 Timber.w("$TAG - method: 警告信息") // 异常但不影响运行 Timber.e(e, "$TAG - method: 错误信息") // 错误并记录异常 ``` #### 【强制】日志格式规范 格式:`类名 - 方法名: 消息` **正例:** ```kotlin Timber.d("$TAG - login: 开始登录") Timber.d("$TAG - login: 登录成功") Timber.e(e, "$TAG - login: 登录失败") ``` **反例:** ```kotlin // ❌ 过多装饰符号和emoji Timber.d("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") Timber.d("📝 [注册] 开始注册请求") Timber.d("🔐 [注册] 密码长度: ${password.length}") ``` #### 【推荐】敏感信息脱敏处理 **反例:** ```kotlin Timber.d("$TAG - login: mobile=$mobile, password=$password") // ❌ 密码不应该打印 ``` **正例:** ```kotlin Timber.d("$TAG - login: mobile=${mobile.take(3)}***, passwordLength=${password.length}") ``` --- ## 三、安全规约 #### 【强制】用户敏感数据必须加密存储 包括但不限于:密码、Token、用户个人信息等。 #### 【强制】用户输入的 SQL 参数严格使用参数绑定或者参数预编译 #### 【推荐】禁止向日志输出用户的密码、Token 等敏感信息 --- ## 四、Android 专项规范 ### 4.1 Activity/Fragment 规范 #### 【强制】Activity 或 Fragment 中使用 ViewBinding/DataBinding **正例:** ```kotlin class LoginFragment : Fragment() { private var _binding: FragmentLoginBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentLoginBinding.inflate(inflater, container, false) return binding.root } override fun onDestroyView() { super.onDestroyView() _binding = null } } ``` #### 【强制】避免在 Activity/Fragment 中直接处理业务逻辑 业务逻辑应该放在 ViewModel 或 UseCase 中。 ### 4.2 ViewModel 规范 #### 【强制】ViewModel 不应该持有 Activity/Fragment 的引用 #### 【推荐】使用 StateFlow/Flow 管理 UI 状态 **正例:** ```kotlin private val _loginState = MutableStateFlow(LoginState.Idle) val loginState: StateFlow = _loginState ``` ### 4.3 资源文件规范 #### 【强制】字符串资源必须定义在 strings.xml 中 **反例:** ```kotlin Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show() // ❌ ``` **正例:** ```kotlin // strings.xml 登录成功 // Kotlin代码 Toast.makeText(context, getString(R.string.login_success), Toast.LENGTH_SHORT).show() ``` --- ## 五、Kotlin 专项规范 ### 5.1 空安全 #### 【强制】优先使用 Kotlin 的空安全特性 **正例:** ```kotlin val name: String? = getUserName() name?.let { println(it.length) } ``` #### 【强制】禁止滥用 `!!` 非空断言操作符 **反例:** ```kotlin val result = response.body()!! // ❌ 危险 ``` **正例:** ```kotlin val result = response.body() ?: return Result.failure(Exception("响应体为空")) ``` ### 5.2 协程规范 #### 【推荐】使用 suspend 函数处理异步操作 **正例:** ```kotlin suspend fun login(mobile: String, password: String): Result { return withContext(Dispatchers.IO) { // 网络请求 } } ``` #### 【强制】在 ViewModel 中使用 viewModelScope **正例:** ```kotlin fun login(mobile: String, password: String) { viewModelScope.launch { repository.login(mobile, password) } } ``` ### 5.3 数据类规范 #### 【推荐】使用 data class 定义数据模型 **正例:** ```kotlin data class LoginRequest( val mobile: String, val password: String ) ``` --- ## 附录:检查清单 ### 代码提交前检查 - [ ] 是否有魔法数字/字符串,已提取为常量 - [ ] 是否使用了 `!!` 非空断言,已改为安全调用 - [ ] 异常处理是否使用了日志框架,而不是 printStackTrace - [ ] 日志是否遵循格式规范(类名 - 方法名: 消息) - [ ] 敏感信息是否已脱敏处理 - [ ] 代码注释是否完整(类、方法) - [ ] 命名是否符合规范(类名大驼峰,方法名小驼峰,常量全大写下划线) - [ ] 硬编码字符串是否已提取到资源文件或常量类 - [ ] 是否有 TODO 未完成,需要补充或删除 --- **文档版本**: v1.0 **最后更新**: 2024-01-01 **维护者**: 开发团队