在Android开发领域摸爬滚打多年后,我深刻体会到:没有规范的代码就像没有交通规则的城市道路。刚开始可能还能应付,但随着项目规模扩大、团队人员增加,各种"撞车事故"就会频繁发生。我曾接手过一个20万行代码的遗留项目,其中光是字符串资源就有7种命名风格,这种混乱直接导致后期维护成本增加了3倍。
编码规范的本质是开发者之间的契约。它通过统一的命名规则、代码结构约定和最佳实践,让不同背景的开发者能够快速理解彼此的代码。根据Google的工程实践统计,采用严格编码规范的项目,其代码审查通过率能提升40%,新成员上手速度能加快60%。
Android项目的包名应该采用逆序域名+模块名的形式。比如公司域名为example.com,那么基础模块包名应为:
code复制com.example.base
重要目录结构规范:
res/layout/:所有布局文件前缀
activity_:Activity布局fragment_:Fragment布局item_:列表项布局res/drawable/:图片资源
ic_:图标类bg_:背景类shape_:形状Drawable类命名采用大驼峰(PascalCase),而方法和变量使用小驼峰(camelCase)。几个容易出错的场景:
布尔类型变量:必须使用is/has/can等前缀
java复制// 正确
boolean isVisible;
boolean hasData;
// 错误
boolean visible;
集合类型:名称应该用复数形式
java复制List<User> userList; // 不推荐
List<User> users; // 推荐
常量:全大写+下划线
java复制static final int MAX_RETRY_COUNT = 3;
建议团队统一采用Android Studio默认的代码格式化配置(基于AOSP风格),关键规则包括:
重要提示:禁止在提交代码时包含非必要的格式化改动,这会导致代码比对困难。应该配置IDE在保存时自动格式化。
我见过最糟糕的代码有两种:完全没有注释的,和全是废话注释的。好的注释应该遵循:
类注释:用JavaDoc格式说明职责
java复制/**
* 处理用户认证相关逻辑,包括登录/注销/令牌刷新
* 需要先初始化{@link #init(Context)}才能使用
*/
public class AuthManager {}
方法注释:必须包含@param和@return
java复制/**
* 加载用户详情
* @param userId 用户ID,不能为空
* @return 用户对象,可能为null
* @throws IllegalArgumentException 当userId无效时抛出
*/
public User loadUser(String userId) {}
行内注释:解释"为什么"而不是"做什么"
java复制// 使用LinkedHashMap保证遍历顺序与插入顺序一致
Map<String, User> userMap = new LinkedHashMap<>();
资源ID的命名应该采用类型_模块_功能的格式:
code复制// 正确示例
R.string.login_btn_confirm
R.color.theme_primary
R.dimen.spacing_medium
// 错误示例
R.string.confirm // 缺少上下文
R.color.red // 过于笼统
常见的几个坑:
xml复制<string name="welcome_message">Hello, %1$s! You have %2$d new messages.</string>
<plurals>xml复制<plurals name="unread_count">
<item quantity="one">You have 1 new message</item>
<item quantity="other">You have %d new messages</item>
</plurals>
样式定义应该遵循继承体系:
xml复制<!-- Base theme -->
<style name="Theme.App" parent="Theme.MaterialComponents.DayNight">
<item name="colorPrimary">@color/theme_primary</item>
</style>
<!-- 派生主题 -->
<style name="Theme.App.Light" parent="Theme.App">
<item name="android:windowLightStatusBar">true</item>
</style>
在Activity/Fragment中最容易犯的生命周期错误:
java复制// 错误示例 - 可能造成内存泄漏
@Override
protected void onStart() {
super.onStart();
locationManager.requestUpdates(location -> {
updateUI(location); // 如果Activity已经stop,这里会崩溃
});
}
// 正确做法
private final LifecycleEventObserver observer = (source, event) -> {
if (event == Lifecycle.Event.ON_START) {
startLocationUpdates();
} else if (event == Lifecycle.Event.ON_STOP) {
stopLocationUpdates();
}
};
@Override
protected void onStart() {
super.onStart();
getLifecycle().addObserver(observer);
}
Android的黄金法则:永远不要在主线程执行耗时操作。但实际项目中常见的问题反而是过度使用线程。建议:
使用协程替代传统线程
kotlin复制viewModelScope.launch {
val data = withContext(Dispatchers.IO) {
repository.loadData()
}
_uiState.value = UiState.Success(data)
}
避免在View层直接管理线程
java复制// 错误示例 - 在Activity中直接创建线程
new Thread(() -> {
Bitmap bitmap = loadImage();
runOnUiThread(() -> imageView.setImageBitmap(bitmap));
}).start();
测试方法名应该遵循被测方法_测试条件_预期结果的格式:
java复制@Test
public void loadUser_withInvalidId_throwsException() {
assertThrows(IllegalArgumentException.class,
() -> userRepository.loadUser(""));
}
@Test
public void calculateDiscount_premiumUser_returns20Percent() {
User user = new User(Type.PREMIUM);
assertEquals(0.2f, calculator.calculateDiscount(user));
}
使用Mockito时的常见问题:
java复制// 错误示例 - 过度mock
@Mock
User user;
@Test
public void testSomething() {
when(user.getName()).thenReturn("Alice");
when(user.getAge()).thenReturn(25);
// 实际测试中根本没用到的这些mock
}
// 正确做法 - 按需mock
@Test
public void testLogin() {
AuthService mockAuth = mock(AuthService.class);
when(mockAuth.login(anyString(), anyString()))
.thenReturn(new User("test"));
LoginViewModel vm = new LoginViewModel(mockAuth);
vm.login("user", "pass");
verify(mockAuth).login("user", "pass");
}
好的Git提交信息应该像新闻标题一样清晰:
code复制feat(login): add biometric authentication support
- Implement FingerprintManager integration
- Add related preference settings
- Update login UI with fingerprint icon
Fixes #123
禁止使用模糊的提交信息如:
code复制update code
fix bug
在审查Android代码时需要特别关注的方面:
RecyclerView的ViewHolder模式常见误用:
java复制// 错误示例 - 每次创建新View
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = inflater.inflate(R.layout.item_user, null);
// ...
return view;
}
// 正确做法 - 复用convertView
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.item_user, parent, false);
}
// ...
return convertView;
}
Application初始化的正确方式:
java复制// 错误示例 - 直接在主线程初始化所有组件
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
initAnalytics(); // 耗时操作
initDatabase();
setupPushService();
}
}
// 正确做法 - 延迟初始化或使用启动器
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
AppStartup.init(this); // 使用Jetpack Startup库
}
}
SharedPreferences的错误用法:
java复制// 错误示例 - 存储敏感数据
SharedPreferences pref = getSharedPreferences("user", MODE_PRIVATE);
pref.edit().putString("password", "123456").apply();
// 正确做法 - 使用EncryptedSharedPreferences
EncryptedSharedPreferences.create(
"secret_prefs",
MasterKey.getOrCreate(this),
this
).edit().putString("token", "abc123").apply();
Retrofit的配置要点:
java复制OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor()) // 仅debug模式启用
.connectTimeout(30, TimeUnit.SECONDS)
.sslSocketFactory(getSSLSocketFactory(), getTrustManager()) // 自定义证书校验
.build();
编码规范不是一成不变的。我们团队每季度会进行一次规范评审,主要关注:
最近一次更新我们就增加了这些内容:
维护规范文档时,建议使用Git版本控制,每个修改都有明确的修改原因和示例代码变更。