22 KiB
22 KiB
代码质量最佳实践
代码质量是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命名约定:
- Activity:以
Activity结尾,如LoginActivity - Fragment:以
Fragment结尾,如UserListFragment - Service:以
Service结尾,如DownloadService - ViewModel:以
ViewModel结尾,如UserViewModel - Repository:以
Repository结尾,如UserRepository - Adapter:以
Adapter结尾,如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" />
资源命名约定:
- 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
代码格式
缩进和空格
// ✅ 好的格式:统一缩进(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>
Ktlint(Kotlin代码规范)
// 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
- 打开项目
cd project
./gradlew build
- 运行项目
项目结构
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应用开发的核心要素。通过遵循代码规范、进行代码审查、定期重构、编写测试和完善文档,可以显著提升代码质量和团队协作效率。
关键要点
- 代码规范:统一的命名、格式和注释规范
- 代码审查:建立审查流程,关注质量、性能和安全
- 代码重构:小步重构,保持测试通过
- 代码测试:编写单元测试和UI测试,提高覆盖率
- 文档编写:完善代码文档和技术文档
相关资源
最后更新:2024年