Files
mkdocs/docs/GoogleAndroid开发文档体系/最佳实践/代码质量最佳实践.md
2026-01-15 16:42:52 +08:00

1098 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码质量最佳实践
代码质量是Android应用开发的核心要素直接影响应用的稳定性、可维护性和团队协作效率。本文档介绍Android开发中的代码质量最佳实践。
## 目录
- [代码规范](#代码规范)
- [代码审查](#代码审查)
- [代码重构](#代码重构)
- [代码测试](#代码测试)
- [文档编写](#文档编写)
---
## 代码规范
### 命名规范
#### 类命名
```java
// ✅ 好的命名清晰、有意义、遵循Android约定
public class MainActivity extends AppCompatActivity { }
public class UserRepository { }
public class LoginViewModel extends ViewModel { }
public class UserAdapter extends RecyclerView.Adapter<UserAdapter.ViewHolder> { }
// ❌ 不好的命名:模糊、无意义、不符合规范
public class Activity1 { }
public class Manager { }
public class Repo { }
public class Adapter1 { }
```
**Android命名约定**
- Activity`Activity`结尾,如`LoginActivity`
- Fragment`Fragment`结尾,如`UserListFragment`
- Service`Service`结尾,如`DownloadService`
- ViewModel`ViewModel`结尾,如`UserViewModel`
- Repository`Repository`结尾,如`UserRepository`
- Adapter`Adapter`结尾,如`UserAdapter`
#### 方法命名
```java
// ✅ 好的命名:动词开头,清晰表达意图
public void loadUserData() { }
public boolean isValidEmail(String email) { }
public User getUserById(String id) { }
public void showErrorMessage(String message) { }
// ❌ 不好的命名:模糊、无意义
public void load() { }
public boolean check(String str) { }
public User get(String id) { }
public void show(String msg) { }
```
#### 变量命名
```java
// ✅ 好的命名:清晰、有意义、遵循驼峰命名
private String userName;
private int userCount;
private List<User> userList;
private boolean isLoggedIn;
private static final String TAG = "MainActivity";
private static final int REQUEST_CODE_LOGIN = 1001;
// ❌ 不好的命名:缩写、无意义、不符合规范
private String un;
private int uc;
private List<User> ul;
private boolean logged; // 布尔值应使用is/has/can前缀
```
#### 资源命名
```xml
<!-- ✅ 好的命名:使用下划线,清晰表达用途 -->
<TextView
android:id="@+id/tv_user_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!-- ❌ 不好的命名:无意义、不符合规范 -->
<TextView
android:id="@+id/text1" />
<Button
android:id="@+id/button1" />
```
**资源命名约定:**
- Layout`activity_xxx.xml``fragment_xxx.xml``item_xxx.xml`
- ID`tv_xxx`TextView`btn_xxx`Button`iv_xxx`ImageView
- String`app_name``login_title``error_message`
- Color`color_primary``color_text_primary`
- Dimension`sp_12``dp_16`
### 代码格式
#### 缩进和空格
```java
// ✅ 好的格式统一缩进4个空格适当空格
public class UserManager {
private String userName;
private UserRepository repository;
public UserManager(UserRepository repository) {
this.repository = repository;
}
public void setUserName(String name) {
if (name != null && !name.isEmpty()) {
this.userName = name;
}
}
}
// ❌ 不好的格式:缩进不一致,空格混乱
public class UserManager{
private String userName;
public void setUserName(String name){
if(name!=null){
this.userName=name;
}
}
}
```
#### 代码组织
```java
// ✅ 好的组织:按逻辑分组,清晰的结构
public class MainActivity extends AppCompatActivity {
// 1. 常量
private static final String TAG = "MainActivity";
private static final int REQUEST_CODE = 1001;
// 2. 成员变量
private TextView tvUserName;
private Button btnLogin;
private UserViewModel viewModel;
// 3. 生命周期方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
initViewModel();
observeData();
}
// 4. 初始化方法
private void initViews() { }
private void initViewModel() { }
private void observeData() { }
// 5. 业务方法
private void handleLogin() { }
private void showError(String message) { }
}
```
#### 注释规范
```java
// ✅ 好的注释:清晰、有用、及时更新
/**
* 获取用户信息
*
* @param userId 用户ID不能为空
* @return 用户信息如果用户不存在返回null
* @throws IllegalArgumentException 如果userId为空或无效
*/
public User getUserInfo(String userId) {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("userId cannot be null or empty");
}
// 从数据库查询用户信息
return userRepository.getUserById(userId);
}
// 复杂逻辑需要注释说明
private void calculatePrice() {
// 计算基础价格
double basePrice = getBasePrice();
// 应用折扣(如果满足条件)
if (isEligibleForDiscount()) {
basePrice *= DISCOUNT_RATE;
}
// 添加税费
double finalPrice = basePrice * (1 + TAX_RATE);
this.price = finalPrice;
}
// ❌ 不好的注释:无用、冗余、过时
// 获取用户信息
public User getUserInfo(String userId) {
// 获取用户信息
return userRepository.getUserById(userId);
}
```
### 代码规范工具
#### Android Lint
```gradle
// build.gradle
android {
lintOptions {
// 检查所有问题
checkAllWarnings true
// 将警告视为错误
warningsAsErrors true
// 生成HTML报告
htmlReport true
htmlOutput file("$project.buildDir/reports/lint/lint-results.html")
}
}
// 运行Lint检查
// ./gradlew lint
```
#### Checkstyle
```gradle
// build.gradle
plugins {
id 'checkstyle'
}
checkstyle {
toolVersion = '10.3.4'
configFile = file("checkstyle.xml")
ignoreFailures = false
}
// checkstyle.xml配置示例
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="TreeWalker">
<module name="MethodName"/>
<module name="ConstantName"/>
<module name="LocalVariableName"/>
</module>
</module>
```
#### KtlintKotlin代码规范
```gradle
// build.gradle.kts
plugins {
id("org.jlleitschuh.gradle.ktlint") version "11.0.0"
}
ktlint {
version.set("0.48.2")
debug.set(true)
verbose.set(true)
android.set(true)
}
// 运行Ktlint检查
// ./gradlew ktlintCheck
```
---
## 代码审查
### 代码审查流程
```java
// 1. 创建功能分支
git checkout -b feature/user-login
git add .
git commit -m "feat: 添加用户登录功能"
// 2. 推送分支
git push origin feature/user-login
// 3. 创建Pull Request/Merge Request
// - 填写清晰的PR描述
// - 添加相关Issue链接
// - 添加审查者
// - 添加标签
// 4. 代码审查
// - 审查者检查代码
// - 提出修改建议
// - 讨论技术方案
// 5. 修改代码
// - 根据审查意见修改
// - 重新提交代码
// - 更新PR
// 6. 合并代码
// - 审查通过后合并
// - 删除功能分支
```
### 代码审查清单
#### 1. 代码质量
```java
// ✅ 好的代码:清晰、简洁、易读
public User getUserById(String userId) {
if (userId == null || userId.isEmpty()) {
return null;
}
return userRepository.getUserById(userId);
}
// ❌ 不好的代码:复杂、难以理解
public User getUserById(String userId) {
if (userId != null) {
if (!userId.isEmpty()) {
User user = userRepository.getUserById(userId);
if (user != null) {
return user;
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
}
```
**检查要点:**
- 代码是否清晰易读?
- 是否有重复代码?
- 方法是否过长建议不超过50行
- 是否有深层嵌套建议不超过3层
#### 2. 性能问题
```java
// ✅ 好的代码:性能优化
// 使用StringBuilder拼接字符串
StringBuilder sb = new StringBuilder();
for (String item : items) {
sb.append(item);
}
String result = sb.toString();
// 使用增强for循环
for (User user : users) {
processUser(user);
}
// ❌ 不好的代码:性能问题
// 字符串拼接性能差
String result = "";
for (String item : items) {
result += item; // 每次拼接都创建新对象
}
// 不必要的循环
for (int i = 0; i < users.size(); i++) {
User user = users.get(i);
processUser(user);
}
```
**检查要点:**
- 是否有性能瓶颈?
- 是否有内存泄漏风险?
- 是否有不必要的对象创建?
- 是否有低效的算法?
#### 3. 安全问题
```java
// ✅ 好的代码:安全处理
// 验证输入
if (password != null && password.length() >= 8) {
// 处理密码
String hashedPassword = hashPassword(password);
}
// 使用HTTPS
OkHttpClient client = new OkHttpClient.Builder()
.certificatePinner(CertificatePinner.DEFAULT)
.build();
// ❌ 不好的代码:安全问题
// 未验证输入
if (password.length() >= 8) { // 可能NullPointerException
// 处理密码
}
// 硬编码敏感信息
String apiKey = "sk-1234567890abcdef"; // 不应该硬编码
```
**检查要点:**
- 是否有安全漏洞?
- 是否验证了用户输入?
- 是否保护了敏感数据?
- 是否使用了安全的网络通信?
#### 4. 可维护性
```java
// ✅ 好的代码:易于维护
// 使用常量
private static final int MIN_PASSWORD_LENGTH = 8;
private static final String ERROR_MESSAGE = "密码长度至少8位";
if (password.length() < MIN_PASSWORD_LENGTH) {
showError(ERROR_MESSAGE);
return;
}
// 提取方法
private void validatePassword(String password) {
if (password == null || password.isEmpty()) {
throw new IllegalArgumentException("密码不能为空");
}
if (password.length() < MIN_PASSWORD_LENGTH) {
throw new IllegalArgumentException(ERROR_MESSAGE);
}
}
// ❌ 不好的代码:难以维护
// 魔法数字
if (password.length() < 8) {
showError("密码长度至少8位");
return;
}
// 重复代码
if (password1.length() < 8) {
showError("密码长度至少8位");
}
if (password2.length() < 8) {
showError("密码长度至少8位");
}
```
**检查要点:**
- 代码是否易于理解和修改?
- 是否有重复代码?
- 是否使用了合适的抽象?
- 是否遵循了SOLID原则
### 代码审查工具
#### GitHub Pull Request
```markdown
## PR模板示例
### 变更描述
- 添加用户登录功能
- 实现记住密码功能
### 测试说明
- [x] 单元测试通过
- [x] UI测试通过
- [x] 手动测试通过
### 截图
[添加相关截图]
### 相关Issue
Closes #123
```
#### Gerrit
```bash
# 提交代码审查
git push origin HEAD:refs/for/master
# 查看审查意见
# 在Gerrit Web界面查看
# 修改代码后重新提交
git commit --amend
git push origin HEAD:refs/for/master
```
---
## 代码重构
### 重构原则
#### 1. 小步重构
```java
// ✅ 好的重构:小步进行,每次只改一点
// 第一步:提取方法
public void processOrder(Order order) {
validateOrder(order);
calculatePrice(order);
saveOrder(order);
}
// 第二步:提取类
public class OrderProcessor {
public void process(Order order) {
validateOrder(order);
calculatePrice(order);
saveOrder(order);
}
}
// ❌ 不好的重构:一次性大改
// 一次性重构整个模块,风险大,难以测试
```
#### 2. 保持测试通过
```java
// ✅ 好的重构:先写测试,再重构
@Test
public void testProcessOrder() {
Order order = new Order();
OrderProcessor processor = new OrderProcessor();
processor.process(order);
assertNotNull(order.getId());
}
// 重构时保持测试通过
public class OrderProcessor {
public void process(Order order) {
// 重构代码,但保持行为不变
}
}
```
#### 3. 识别代码坏味道
```java
// 1. 长方法Long Method
// ❌ 坏味道:方法过长
public void processUserData() {
// 100行代码...
}
// ✅ 重构:拆分为多个小方法
public void processUserData() {
validateUserData();
transformUserData();
saveUserData();
}
// 2. 重复代码Duplicated Code
// ❌ 坏味道:重复代码
public void processUser1() {
if (user1 != null) {
user1.setStatus("active");
saveUser(user1);
}
}
public void processUser2() {
if (user2 != null) {
user2.setStatus("active");
saveUser(user2);
}
}
// ✅ 重构:提取公共方法
public void processUser(User user) {
if (user != null) {
user.setStatus("active");
saveUser(user);
}
}
// 3. 大类Large Class
// ❌ 坏味道:类职责过多
public class UserManager {
// 用户管理
// 订单管理
// 支付管理
// ...
}
// ✅ 重构:拆分职责
public class UserManager { }
public class OrderManager { }
public class PaymentManager { }
```
### 常见重构技巧
#### 1. 提取方法Extract Method
```java
// 重构前
public void printUserInfo(User user) {
System.out.println("Name: " + user.getName());
System.out.println("Email: " + user.getEmail());
System.out.println("Age: " + user.getAge());
}
// 重构后
public void printUserInfo(User user) {
printName(user);
printEmail(user);
printAge(user);
}
private void printName(User user) {
System.out.println("Name: " + user.getName());
}
private void printEmail(User user) {
System.out.println("Email: " + user.getEmail());
}
private void printAge(User user) {
System.out.println("Age: " + user.getAge());
}
```
#### 2. 提取类Extract Class
```java
// 重构前
public class User {
private String name;
private String email;
private String street;
private String city;
private String zipCode;
}
// 重构后
public class User {
private String name;
private String email;
private Address address;
}
public class Address {
private String street;
private String city;
private String zipCode;
}
```
#### 3. 引入参数对象Introduce Parameter Object
```java
// 重构前
public void createUser(String name, String email, String phone, String address) {
// ...
}
// 重构后
public void createUser(UserInfo userInfo) {
// ...
}
public class UserInfo {
private String name;
private String email;
private String phone;
private String address;
}
```
### 重构工具
#### Android Studio重构功能
```java
// 右键菜单 -> Refactor
// - Rename重命名
// - Extract Method提取方法
// - Extract Variable提取变量
// - Extract Field提取字段
// - Extract Interface提取接口
// - Extract Superclass提取父类
// - Inline内联
// - Move移动
// - Safe Delete安全删除
```
---
## 代码测试
### 测试金字塔
```
/\
/ \
/集成测试\
/----------\
/ 单元测试 \
/--------------\
```
- **单元测试**70%):快速、隔离、覆盖核心逻辑
- **集成测试**20%):测试组件间交互
- **UI测试**10%):测试用户界面
### 单元测试
#### JUnit测试
```java
// UserRepositoryTest.java
@RunWith(MockitoJUnitRunner.class)
public class UserRepositoryTest {
@Mock
private UserDao userDao;
@InjectMocks
private UserRepository repository;
@Test
public void getUserById_shouldReturnUser_whenUserExists() {
// Given
String userId = "123";
User expectedUser = new User(userId, "John");
when(userDao.getUserById(userId)).thenReturn(expectedUser);
// When
User result = repository.getUserById(userId);
// Then
assertNotNull(result);
assertEquals("John", result.getName());
verify(userDao).getUserById(userId);
}
@Test
public void getUserById_shouldReturnNull_whenUserNotExists() {
// Given
String userId = "999";
when(userDao.getUserById(userId)).thenReturn(null);
// When
User result = repository.getUserById(userId);
// Then
assertNull(result);
}
}
```
#### Mockito使用
```java
// Mock对象
@Mock
private UserService userService;
// Stub方法
when(userService.getUser("123")).thenReturn(new User("123", "John"));
// 验证调用
verify(userService).getUser("123");
verify(userService, times(2)).getUser("123");
verify(userService, never()).deleteUser("123");
// 参数匹配
verify(userService).getUser(anyString());
verify(userService).getUser(eq("123"));
```
### UI测试
#### Espresso测试
```java
@RunWith(AndroidJUnit4.class)
public class LoginActivityTest {
@Rule
public ActivityScenarioRule<LoginActivity> activityRule =
new ActivityScenarioRule<>(LoginActivity.class);
@Test
public void testLogin_success() {
// 输入用户名
onView(withId(R.id.et_username))
.perform(typeText("testuser"));
// 输入密码
onView(withId(R.id.et_password))
.perform(typeText("password123"));
// 点击登录按钮
onView(withId(R.id.btn_login))
.perform(click());
// 验证跳转到主页面
intended(hasComponent(MainActivity.class.getName()));
}
@Test
public void testLogin_failure() {
// 输入错误的用户名和密码
onView(withId(R.id.et_username))
.perform(typeText("wronguser"));
onView(withId(R.id.et_password))
.perform(typeText("wrongpass"));
// 点击登录按钮
onView(withId(R.id.btn_login))
.perform(click());
// 验证显示错误消息
onView(withId(R.id.tv_error))
.check(matches(isDisplayed()));
}
}
```
### 测试覆盖率
```gradle
// build.gradle
android {
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
// 生成测试覆盖率报告
// ./gradlew createDebugCoverageReport
// 报告位置build/reports/coverage/debug/index.html
```
### 测试最佳实践
```java
// ✅ 好的测试:清晰、独立、快速
@Test
public void calculateTotalPrice_shouldReturnCorrectSum() {
// Arrange
List<Item> items = Arrays.asList(
new Item("A", 10.0),
new Item("B", 20.0)
);
PriceCalculator calculator = new PriceCalculator();
// Act
double total = calculator.calculateTotal(items);
// Assert
assertEquals(30.0, total, 0.01);
}
// ❌ 不好的测试:依赖外部、慢、不清晰
@Test
public void test() {
// 测试代码混乱,依赖网络、数据库等
}
```
---
## 文档编写
### 代码文档
#### JavaDoc注释
```java
/**
* 用户管理类,提供用户的增删改查功能
*
* <p>使用示例:
* <pre>{@code
* UserManager manager = new UserManager();
* User user = manager.getUserById("123");
* }</pre>
*
* @author John Doe
* @version 1.0
* @since 1.0
*/
public class UserManager {
/**
* 根据用户ID获取用户信息
*
* @param userId 用户ID不能为空
* @return 用户信息如果用户不存在返回null
* @throws IllegalArgumentException 如果userId为空
* @see User
*/
public User getUserById(String userId) {
if (userId == null || userId.isEmpty()) {
throw new IllegalArgumentException("userId cannot be null or empty");
}
return userRepository.getUserById(userId);
}
}
```
#### README文档
```markdown
# 项目名称
项目简要描述
## 功能特性
- 功能1
- 功能2
- 功能3
## 快速开始
### 环境要求
- Android Studio Arctic Fox+
- JDK 8+
- Android SDK API 21+
### 安装步骤
1. 克隆项目
```bash
git clone https://github.com/example/project.git
```
2. 打开项目
```bash
cd project
./gradlew build
```
3. 运行项目
## 项目结构
```
app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ ├── res/
│ │ └── AndroidManifest.xml
│ └── test/
└── build.gradle
```
## API文档
[API文档链接]
## 贡献指南
[贡献指南内容]
## 许可证
[许可证信息]
```
### 技术文档
#### 架构文档
```markdown
# 应用架构文档
## 架构概览
应用采用MVVM架构模式
## 模块划分
### 1. UI层
- Activity/Fragment
- ViewModel
- Adapter
### 2. 业务层
- UseCase
- Repository
### 3. 数据层
- DataSource
- API Service
- Database
## 数据流
[数据流图]
## 技术选型
- 架构MVVM
- 依赖注入Dagger/Hilt
- 网络Retrofit + OkHttp
- 数据库Room
```
#### API文档
```markdown
# API文档
## 用户相关API
### 获取用户信息
**请求**
```
GET /api/user/{userId}
```
**响应**
```json
{
"id": "123",
"name": "John Doe",
"email": "john@example.com"
}
```
### 更新用户信息
**请求**
```
PUT /api/user/{userId}
Content-Type: application/json
{
"name": "Jane Doe",
"email": "jane@example.com"
}
```
```
### 文档维护
```markdown
## 文档维护清单
- [ ] 代码变更时更新相关文档
- [ ] 定期检查文档准确性
- [ ] 保持文档与代码同步
- [ ] 添加必要的示例代码
- [ ] 更新版本号和日期
```
---
## 总结
代码质量是Android应用开发的核心要素。通过遵循代码规范、进行代码审查、定期重构、编写测试和完善文档可以显著提升代码质量和团队协作效率。
### 关键要点
1. **代码规范**:统一的命名、格式和注释规范
2. **代码审查**:建立审查流程,关注质量、性能和安全
3. **代码重构**:小步重构,保持测试通过
4. **代码测试**编写单元测试和UI测试提高覆盖率
5. **文档编写**:完善代码文档和技术文档
### 相关资源
- [Android代码规范指南](https://developer.android.com/kotlin/style-guide)
- [Android测试指南](https://developer.android.com/training/testing)
- [Android架构指南](https://developer.android.com/topic/architecture)
---
*最后更新2024年*