1098 lines
22 KiB
Markdown
1098 lines
22 KiB
Markdown
# 代码质量最佳实践
|
||
|
||
代码质量是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>
|
||
```
|
||
|
||
#### Ktlint(Kotlin代码规范)
|
||
|
||
```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年*
|