作为一名在Android开发领域摸爬滚打多年的老码农,我深知编码规范的重要性。这份《Android编码规范(修订版)》是我在参与过17个商业项目后,结合Google官方指南、行业最佳实践和团队踩坑经验整理而成的实战手册。不同于学院派的教条式规范,这里每一条规则背后都有血淋淋的教训和真实的性能数据支撑。
在大型项目协作中,我们曾因命名混乱导致合并冲突频发,因资源管理不当引发内存泄漏,更因架构分层模糊造成功能迭代举步维艰。这份规范就是要用最直白的语言,告诉你哪些红线绝对不能碰,哪些技巧能让你少加三天班。
类名采用大驼峰(MainActivity),变量用小驼峰(userName),常量全大写加下划线(MAX_RETRY_COUNT)——这些基础规则大家耳熟能详。但真正容易翻车的是这些场景:
控件ID命名:用btnSubmit而不是button1,这样在团队协作时,别人在布局文件里看到android:id="@+id/btnSubmit"就能立即理解用途。我们曾有个项目因为大量使用textView1这类命名,导致简单的UI改动都要全局搜索确认控件用途。
资源文件命名:颜色资源用color_primary而不是red,尺寸用dimen_padding_medium而非padding_16。有个项目因为直接使用blue作为主题色命名,后来品牌色变更时,开发者在colors.xml里发现了20多个不同色值的blue定义。
重要提示:在Android Studio中开启"Hardcoded values"检测,能自动发现未使用资源引用的硬编码值
包结构组织建议按功能而非类型划分:
code复制com.company.app
├── auth # 所有认证相关
│ ├── view
│ ├── model
│ └── service
└── product # 商品模块
├── detail
└── list
对比传统的按类型分包(把所有Activity放一个包),这种结构在模块化时迁移成本降低70%。我们重构一个20万行代码的项目时,按功能分包使模块拆分时间从3周缩短到5天。
类长度控制:
有个支付模块的Activity膨胀到2400行后,每次修改都要在onCreate里滚动5分钟才能找到目标代码。后来我们采用"一个屏幕原则"——所有关键逻辑应该在不滚动屏幕的情况下可见。
java复制// 错误示范
while(condition) {
String result = new String(data);
}
// 正确做法
String result;
while(condition) {
result = new String(data);
}
在低端设备测试中,错误写法会导致GC频率增加3倍。
不要在onDraw()里分配内存:
我们有个自定义View因为每次绘制都新建Paint对象,导致列表滚动时频繁GC,帧率从60骤降到22。
不要滥用静态变量:
有个全局缓存的static List<User>导致用户数据无法及时更新。建议使用ViewModel或Repository模式管理数据。
Dispatchers.IODispatchers.DefaultDispatchers.Main我们封装了安全的协程调用模板:
kotlin复制fun safeLaunch(block: suspend () -> Unit) {
viewModelScope.launch {
try {
withContext(Dispatchers.IO) {
block()
}
} catch (e: Exception) {
// 统一错误处理
}
}
}
code复制Presentation Layer (UI)
│
├── ViewModel
│ └── 仅处理UI逻辑
│
Domain Layer (业务核心)
│ ├── UseCase
│ └── 纯Kotlin/Java
│
Data Layer
├── Repository
└── 数据源管理
严禁出现以下情况:
@Parcelize)模块间通信必须通过以下方式:
app://feature/page?id=1)FeatureApi接口+FeatureProvider)EventBus(带生命周期感知的封装版)我们曾因直接引用模块代码导致循环依赖,解耦花了2周时间。现在强制要求:
kotlin复制// 错误:直接依赖实现
implementation(project(":feature"))
// 正确:依赖接口模块
implementation(project(":feature-api"))
使用JaCoCo配置最低阈值:
gradle复制jacoco {
toolVersion = "0.8.7"
reportsDirectory = file("$buildDir/reports")
}
android {
testOptions {
unitTests.all {
jacoco {
includeNoLocationClasses = true
excludes = ['jdk.internal.*']
}
}
}
}
ComposeTestRule测试Compose UIThread.sleep(),用waitUntil@Before初始化我们总结的页面对象模式:
kotlin复制class LoginPage(private val composeTestRule: ComposeTestRule) {
fun enterEmail(text: String) {
composeTestRule.onNodeWithTag("email_field").performTextInput(text)
}
fun clickSubmit() {
composeTestRule.onNodeWithText("Submit").performClick()
}
}
必须包含以下检查步骤:
Git Hook配置示例:
bash复制#!/bin/sh
./gradlew ktlintCheck
if [ $? -ne 0 ]; then
echo "代码格式检查失败"
exit 1
fi
我们配置的Jenkins关键阶段:
groovy复制stage('Build') {
steps {
sh './gradlew assembleRelease'
archiveArtifacts '**/*.apk'
}
post {
always {
junit '**/test-results/**/*.xml'
}
}
}
必须配置未捕获异常处理器:
kotlin复制Thread.setDefaultUncaughtExceptionHandler { thread, ex ->
FirebaseCrashlytics.getInstance().recordException(ex)
// 跳转到错误恢复页面
Intent(baseContext, ErrorActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
putExtra("error", ex.stackTraceToString())
startActivity(this)
}
Process.killProcess(Process.myPid())
}
统一错误响应封装:
kotlin复制sealed class ApiResult<out T> {
data class Success<T>(val data: T) : ApiResult<T>()
data class Error(
val code: Int,
val message: String? = null
) : ApiResult<Nothing>()
}
使用示例:
kotlin复制when(val result = api.getData()) {
is ApiResult.Success -> updateUI(result.data)
is ApiResult.Error -> showErrorDialog(result.message)
}
使用@RequiresApi和版本检查:
kotlin复制@RequiresApi(Build.VERSION_CODES.O)
fun newApiMethod() {
// Android 8.0+专属实现
}
fun legacyMethod() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
newApiMethod()
} else {
// 兼容实现
}
}
在根build.gradle定义版本:
gradle复制ext {
kotlin_version = '1.7.10'
androidx_core_version = '1.9.0'
}
模块引用方式:
gradle复制implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "androidx.core:core-ktx:$androidx_core_version"
EncryptedSharedPreferencesAndroidKeyStore加密存储示例:
kotlin复制val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val sharedPreferences = EncryptedSharedPreferences.create(
context,
"secret_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
强制要求:
network_security_config.xml示例:
xml复制<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">yourdomain.com</domain>
<pin-set>
<pin digest="SHA-256">YourBase64Pin</pin>
</pin-set>
</domain-config>
</network-security-config>
内存泄漏风险:
线程安全:
架构合规:
必备AS插件:
build.gradle配置:
gradle复制debug {
applicationIdSuffix ".debug"
debuggable true
// 启用严格模式
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
在团队中推行这套规范后,我们的代码CR通过率从35%提升到82%,线上崩溃率下降64%。记住,好的规范不是束缚,而是让你避免重蹈前人覆辙的防护栏。当遇到规范未覆盖的特殊情况时,记住这条终极原则:假设三个月后别人接手你的代码时,他能否在10分钟内理解并安全修改这段代码?