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

22 KiB
Raw Blame History

代码质量最佳实践

代码质量是Android应用开发的核心要素直接影响应用的稳定性、可维护性和团队协作效率。本文档介绍Android开发中的代码质量最佳实践。

目录


代码规范

命名规范

类命名

// ✅ 好的命名清晰、有意义、遵循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命名约定

  • ActivityActivity结尾,如LoginActivity
  • FragmentFragment结尾,如UserListFragment
  • ServiceService结尾,如DownloadService
  • ViewModelViewModel结尾,如UserViewModel
  • RepositoryRepository结尾,如UserRepository
  • AdapterAdapter结尾,如UserAdapter

方法命名

// ✅ 好的命名:动词开头,清晰表达意图
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) { }

变量命名

// ✅ 好的命名:清晰、有意义、遵循驼峰命名
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前缀

资源命名

<!-- ✅ 好的命名:使用下划线,清晰表达用途 -->
<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" />

资源命名约定:

  • Layoutactivity_xxx.xmlfragment_xxx.xmlitem_xxx.xml
  • IDtv_xxxTextViewbtn_xxxButtoniv_xxxImageView
  • Stringapp_namelogin_titleerror_message
  • Colorcolor_primarycolor_text_primary
  • Dimensionsp_12dp_16

代码格式

缩进和空格

// ✅ 好的格式统一缩进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;
}
}
}

代码组织

// ✅ 好的组织:按逻辑分组,清晰的结构
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) { }
}

注释规范

// ✅ 好的注释:清晰、有用、及时更新
/**
 * 获取用户信息
 * 
 * @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

// build.gradle
android {
    lintOptions {
        // 检查所有问题
        checkAllWarnings true
        // 将警告视为错误
        warningsAsErrors true
        // 生成HTML报告
        htmlReport true
        htmlOutput file("$project.buildDir/reports/lint/lint-results.html")
    }
}

// 运行Lint检查
// ./gradlew lint

Checkstyle

// 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代码规范

// 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

代码审查

代码审查流程

// 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. 代码质量

// ✅ 好的代码:清晰、简洁、易读
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. 性能问题

// ✅ 好的代码:性能优化
// 使用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. 安全问题

// ✅ 好的代码:安全处理
// 验证输入
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. 可维护性

// ✅ 好的代码:易于维护
// 使用常量
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

## PR模板示例

### 变更描述
- 添加用户登录功能
- 实现记住密码功能

### 测试说明
- [x] 单元测试通过
- [x] UI测试通过
- [x] 手动测试通过

### 截图
[添加相关截图]

### 相关Issue
Closes #123

Gerrit

# 提交代码审查
git push origin HEAD:refs/for/master

# 查看审查意见
# 在Gerrit Web界面查看

# 修改代码后重新提交
git commit --amend
git push origin HEAD:refs/for/master

代码重构

重构原则

1. 小步重构

// ✅ 好的重构:小步进行,每次只改一点
// 第一步:提取方法
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. 保持测试通过

// ✅ 好的重构:先写测试,再重构
@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. 识别代码坏味道

// 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

// 重构前
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

// 重构前
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

// 重构前
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重构功能

// 右键菜单 -> Refactor
// - Rename重命名
// - Extract Method提取方法
// - Extract Variable提取变量
// - Extract Field提取字段
// - Extract Interface提取接口
// - Extract Superclass提取父类
// - Inline内联
// - Move移动
// - Safe Delete安全删除

代码测试

测试金字塔

        /\
       /  \
      /集成测试\
     /----------\
    /  单元测试   \
   /--------------\
  • 单元测试70%):快速、隔离、覆盖核心逻辑
  • 集成测试20%):测试组件间交互
  • UI测试10%):测试用户界面

单元测试

JUnit测试

// 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使用

// 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测试

@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()));
    }
}

测试覆盖率

// build.gradle
android {
    buildTypes {
        debug {
            testCoverageEnabled = true
        }
    }
}

// 生成测试覆盖率报告
// ./gradlew createDebugCoverageReport
// 报告位置build/reports/coverage/debug/index.html

测试最佳实践

// ✅ 好的测试:清晰、独立、快速
@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注释

/**
 * 用户管理类,提供用户的增删改查功能
 * 
 * <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文档

# 项目名称

项目简要描述

## 功能特性

- 功能1
- 功能2
- 功能3

## 快速开始

### 环境要求

- Android Studio Arctic Fox+
- JDK 8+
- Android SDK API 21+

### 安装步骤

1. 克隆项目
```bash
git clone https://github.com/example/project.git
  1. 打开项目
cd project
./gradlew build
  1. 运行项目

项目结构

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文档

# 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. 文档编写:完善代码文档和技术文档

相关资源


最后更新2024年