# 新大洲 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<LoginState>
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>(LoginState.Idle)
    val loginState: StateFlow<LoginState> = _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<LoginResponse> 登录结果，成功包含登录响应数据
     */
    suspend fun login(mobile: String, password: String): Result<LoginResponse> {
        // ...
    }
}
```

#### 【强制】所有的抽象方法（包括接口中的方法）必须要用 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>(LoginState.Idle)
val loginState: StateFlow<LoginState> = _loginState
```

### 4.3 资源文件规范

#### 【强制】字符串资源必须定义在 strings.xml 中

**反例：**
```kotlin
Toast.makeText(context, "登录成功", Toast.LENGTH_SHORT).show()  // ❌
```

**正例：**
```kotlin
// strings.xml
<string name="login_success">登录成功</string>

// 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<LoginResponse> {
    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  
**维护者**: 开发团队

