关于 android-native-dev
android-native-dev 是面向 Android 应用开发的技能,涵盖 Kotlin 与 Jetpack Compose。它为开发者提供项目配置、编码规范、界面实践和 Gradle 构建排错指导。
主要功能
- 01Android 项目初始化与 Gradle 配置
- 02Kotlin 编码与协程实践
- 03Jetpack Compose 状态和界面指导
- 04构建变体与产品风味配置
- 05构建错误诊断与调试
适用场景
- 01从空目录启动 Android 项目
- 02为开发、预发布和生产版本添加产品风味
- 03检查 Kotlin 与 Compose 代码中的常见问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ minimax-ai/ skills --skill android-native-dev安装后在 Claude Code 中输入/android-native-dev确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/accessibility.mdadaptive-screens.mddesign-style-guide.mdfunctional-requirements.mdmotion-system.mdperformance-stability.mdprivacy-security.mdtesting.mdvisual-design.md详细介绍
1. 项目场景评估
开始开发前,先评估当前项目状态:
| 场景 | 特征 | 方法 |
|---|---|---|
| 空目录 | 不存在任何文件 | 需要完整初始化,包括 Gradle Wrapper |
| 已有 Gradle Wrapper | 存在 gradlew 和 gradle/wrapper/ | 直接使用 ./gradlew 构建 |
| Android Studio 项目 | 项目结构完整,可能缺少 wrapper | 检查 wrapper,如有需要,运行 gradle wrapper |
| 项目不完整 | 存在部分文件 | 检查缺少的文件,补全配置 |
关键原则:
- 编写业务逻辑前,确保
./gradlew assembleDebug能够成功运行 - 如果缺少
gradle.properties,先创建该文件并配置 AndroidX
1.1 必需文件检查清单
MyApp/
├── gradle.properties # Configure AndroidX and other settings
├── settings.gradle.kts
├── build.gradle.kts # Root level
├── gradle/wrapper/
│ └── gradle-wrapper.properties
├── app/
│ ├── build.gradle.kts # Module level
│ └── src/main/
│ ├── AndroidManifest.xml
│ ├── java/com/example/myapp/
│ │ └── MainActivity.kt
│ └── res/
│ ├── values/
│ │ ├── strings.xml
│ │ ├── colors.xml
│ │ └── themes.xml
│ └── mipmap-*/ # App icons
2. 项目配置
2.1 gradle.properties
# Required configuration
android.useAndroidX=true
android.enableJetifier=true
# Build optimization
org.gradle.parallel=true
kotlin.code.style=official
# JVM memory settings (adjust based on project size)
# Small projects: 2048m, Medium: 4096m, Large: 8192m+
# org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
注意:如果构建时遇到
OutOfMemoryError,请增大-Xmx的值。依赖较多的大型项目可能需要 8GB 或更多。
2.2 依赖声明规范
dependencies {
// Use BOM to manage Compose versions
implementation(platform("androidx.compose:compose-bom:2024.02.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
// Activity & ViewModel
implementation("androidx.activity:activity-compose:1.8.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
}
2.3 构建变体和产品风味
产品风味可用于创建应用的不同版本(例如免费版/付费版、开发版/预发布版/生产版)。
在 app/build.gradle.kts 中配置:
android {
// Define flavor dimensions
flavorDimensions += "environment"
productFlavors {
create("dev") {
dimension = "environment"
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
// Different config values per flavor
buildConfigField("String", "API_BASE_URL", "\"https://dev-api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
// Different resources
resValue("string", "app_name", "MyApp Dev")
}
create("staging") {
dimension = "environment"
applicationIdSuffix = ".staging"
versionNameSuffix = "-staging"
buildConfigField("String", "API_BASE_URL", "\"https://staging-api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "true")
resValue("string", "app_name", "MyApp Staging")
}
create("prod") {
dimension = "environment"
// No suffix for production
buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"")
buildConfigField("Boolean", "ENABLE_LOGGING", "false")
resValue("string", "app_name", "MyApp")
}
}
buildTypes {
debug {
isDebuggable = true
isMinifyEnabled = false
}
release {
isDebuggable = false
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
}
构建变体命名:{flavor}{BuildType} → 例如 devDebug、prodRelease
Gradle 构建命令:
# List all available build variants
./gradlew tasks --group="build"
# Build specific variant (flavor + buildType)
./gradlew assembleDevDebug # Dev flavor, Debug build
./gradlew assembleStagingDebug # Staging flavor, Debug build
./gradlew assembleProdRelease # Prod flavor, Release build
# Build all variants of a specific flavor
./gradlew assembleDev # All Dev variants (debug + release)
./gradlew assembleProd # All Prod variants
# Build all variants of a specific build type
./gradlew assembleDebug # All flavors, Debug build
./gradlew assembleRelease # All flavors, Release build
# Install specific variant to device
./gradlew installDevDebug
./gradlew installProdRelease
# Build and install in one command
./gradlew installDevDebug && adb shell am start -n com.example.myapp.dev/.MainActivity
在代码中访问 BuildConfig:
注意:从 AGP 8.0 开始,默认不再生成
BuildConfig。必须在build.gradle.kts中显式启用:android { buildFeatures { buildConfig = true } }
// Use build config values in your code
val apiUrl = BuildConfig.API_BASE_URL
val isLoggingEnabled = BuildConfig.ENABLE_LOGGING
if (BuildConfig.DEBUG) {
// Debug-only code
}
特定风味的源集:
app/src/
├── main/ # Shared code for all flavors
├── dev/ # Dev-only code and resources
│ ├── java/
│ └── res/
├── staging/ # Staging-only code and resources
├── prod/ # Prod-only code and resources
├── debug/ # Debug build type code
└── release/ # Release build type code
多个风味维度(例如环境 + 等级):
android {
flavorDimensions += listOf("environment", "tier")
productFlavors {
create("dev") { dimension = "environment" }
create("prod") { dimension = "environment" }
create("free") { dimension = "tier" }
create("paid") { dimension = "tier" }
}
}
// Results in: devFreeDebug, devPaidDebug, prodFreeRelease, etc.
3. Kotlin 开发规范
3.1 命名约定
| 类型 | 约定 | 示例 |
|---|---|---|
| 类/接口 | PascalCase | UserRepository、MainActivity |
| 函数/变量 | camelCase | getUserName()、isLoading |
| 常量 | SCREAMING_SNAKE | MAX_RETRY_COUNT |
| 包 | 小写 | com.example.myapp |
| Composable | PascalCase | @Composable fun UserCard() |
3.2 代码规范(重要)
空安全:
// ❌ Avoid: Non-null assertion !! (may crash)
val name = user!!.name
// ✅ Recommended: Safe call + default value
val name = user?.name ?: "Unknown"
// ✅ Recommended: let handling
user?.let { processUser(it) }
异常处理:
// ❌ Avoid: Random try-catch in business layer swallowing exceptions
fun loadData() {
try {
val data = api.fetch()
} catch (e: Exception) {
// Swallowing exception, hard to debug
}
}
// ✅ Recommended: Let exceptions propagate, handle at appropriate layer
suspend fun loadData(): Result<Data> {
return try {
Result.success(api.fetch())
} catch (e: Exception) {
Result.failure(e) // Wrap and return, let caller decide handling
}
}
// ✅ Recommended: Unified handling in ViewModel
viewModelScope.launch {
runCatching { repository.loadData() }
.onSuccess { _uiState.value = UiState.Success(it) }
.onFailure { _uiState.value = UiState.Error(it.message) }
}
3.3 线程与协程(关键)
线程选择原则:
| 操作类型 | 线程 | 说明 |
|---|---|---|
| UI 更新 | Dispatchers.Main | 更新 View、State、LiveData |
| 网络请求 | Dispatchers.IO | HTTP 调用、API 请求 |
| 文件 I/O | Dispatchers.IO | 本地存储、数据库操作 |
| 计算密集型任务 | Dispatchers.Default | JSON 解析、排序、加密 |
正确用法:
// In ViewModel
viewModelScope.launch {
// Default Main thread, can update UI State
_uiState.value = UiState.Loading
// Switch to IO thread for network request
val result = withContext(Dispatchers.IO) {
repository.fetchData()
}
// Automatically returns to Main thread, update UI
_uiState.value = UiState.Success(result)
}
// In Repository (suspend functions should be main-safe)
suspend fun fetchData(): Data = withContext(Dispatchers.IO) {
api.getData()
}
常见错误:
// ❌ Wrong: Updating UI on IO thread
viewModelScope.launch(Dispatchers.IO) {
val data = api.fetch()
_uiState.value = data // Crash or warning!
}
// ❌ Wrong: Executing time-consuming operation on Main thread
viewModelScope.launch {
val data = api.fetch() // Blocking main thread! ANR
}
// ✅ Correct: Fetch on IO, update on Main
viewModelScope.launch {
val data = withContext(Dispatchers.IO) { api.fetch() }
_uiState.value = data
}
3.4 可见性规则
// Default is public, declare explicitly when needed
class UserRepository { // public
private val cache = mutableMapOf<String, User>() // Visible only within class
internal fun clearCache() {} // Visible only within module
}
// data class properties are public by default, be careful when used across modules
data class User(
val id: String, // public
val name: String
)
3.5 常见语法问题
// ❌ Wrong: Accessing uninitialized lateinit
class MyViewModel : ViewModel() {
lateinit var data: String
fun process() = data.length // May crash
}
// ✅ Correct: Use nullable or default value
class MyViewModel : ViewModel() {
var data: String? = null
fun process() = data?.length ?: 0
}
// ❌ Wrong: Using return in lambda
list.forEach { item ->
if (item.isEmpty()) return // Returns from outer function!
}
// ✅ Correct: Use return@forEach
list.forEach { item ->
if (item.isEmpty()) return@forEach
}
3.6 服务端响应数据类的字段必须可空
// ❌ Wrong: Fields declared as non-null (server may not return them)
data class UserResponse(
val id: String = "",
val name: String = "",
val avatar: String = ""
)
// ✅ Correct: All fields declared as nullable
data class UserResponse(
@SerializedName("id")
val id: String? = null,
@SerializedName("name")
val name: String? = null,
@SerializedName("avatar")
val avatar: String? = null
)
3.7 生命周期资源管理
// ❌ Wrong: Only adding Observer, not removing
class MyView : View {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
activity?.lifecycle?.addObserver(this)
}
// Memory leak!
}
// ✅ Correct: Paired add and remove
class MyView : View {
override fun onAttachedToWindow() {
super.onAttachedToWindow()
activity?.lifecycle?.addObserver(this)
}
override fun onDetachedFromWindow() {
activity?.lifecycle?.removeObserver(this)
super.onDetachedFromWindow()
}
}
3.8 日志级别用法
import android.util.Log
// Info: Key checkpoints in normal flow
Log.i(TAG, "loadData: started, userId = $userId")
// Warning: Abnormal but recoverable situations
Log.w(TAG, "loadData: cache miss, fallback to network")
// Error: Failure/error situations
Log.e(TAG, "loadData failed: ${error.message}")
| 级别 | 使用场景 |
|---|---|
i (Info) | 正常流程、方法入口、关键参数 |
w (Warning) | 可恢复的异常、回退处理、返回 null |
e (Error) | 请求失败、捕获到的异常、不可恢复的错误 |
4. Jetpack Compose 规范
4.1 @Composable 上下文规则
// ❌ Wrong: Calling Composable from non-Composable function
fun showError(message: String) {
Text(message) // Compile error!
}
// ✅ Correct: Mark as @Composable
@Composable
fun ErrorMessage(message: String) {
Text(message)
}
// ❌ Wrong: Using suspend outside LaunchedEffect
@Composable
fun MyScreen() {
val data = fetchData() // Error!
}
// ✅ Correct: Use LaunchedEffect
@Composable
fun MyScreen() {
var data by remember { mutableStateOf<Data?>(null) }
LaunchedEffect(Unit) {
data = fetchData()
}
}
4.2 状态管理
// Basic State
var count by remember { mutableStateOf(0) }
// Derived State (avoid redundant computation)
val isEven by remember { derivedStateOf { count % 2 == 0 } }
// Persist across recomposition (e.g., scroll position)
val scrollState = rememberScrollState()
// State in ViewModel
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
}
4.3 Compose 常见错误
// ❌ Wrong: Creating objects in Composable (created on every recomposition)
@Composable
fun MyScreen() {
val viewModel = MyViewModel() // Wrong!
}
// ✅ Correct: Use viewModel() or remember
@Composable
fun MyScreen(viewModel: MyViewModel = viewModel()) {
// ...
}
5. 资源与图标
5.1 应用图标要求
必须提供多种分辨率的图标:
| 目录 | 尺寸 | 用途 |
|---|---|---|
| mipmap-mdpi | 48x48 | 基准 |
| mipmap-hdpi | 72x72 | 1.5x |
| mipmap-xhdpi | 96x96 | 2x |
| mipmap-xxhdpi | 144x144 | 3x |
| mipmap-xxxhdpi | 192x192 | 4x |
建议使用自适应图标(Android 8+):
<adaptive-icon>
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>
5.2 资源命名约定
| 类型 | 前缀 | 示例 |
|---|---|---|
| 布局 | layout_ | layout_main.xml |
| 图像 | ic_、img_、bg_ | ic_user.png |
| 颜色 | color_ | color_primary |
| 字符串 | - | app_name、btn_submit |
5.3 避免使用 Android 保留名称(重要)
变量名、资源 ID、颜色、图标和 XML 元素不得使用 Android 保留字或系统资源名称。使用保留名称会导致构建错误或资源冲突。
应避免使用的常见保留名称:
| 类别 | 保留名称(请勿使用) |
|---|---|
| 颜色 | background、foreground、transparent、white、black |
| 图标/可绘制对象 | icon、logo、image、drawable |
| 视图 | view、text、button、layout、container |
| 属性 | id、name、type、style、theme、color |
| 系统 | app、android、content、data、action |
示例:
<color name="background">#FFFFFF</color>
<color name="icon">#000000</color>
<color name="app_background">#FFFFFF</color>
<color name="icon_primary">#000000</color>
// ❌ Wrong: Variable names conflict with system
val icon = R.drawable.my_icon
val background = Color.White
// ✅ Correct: Use descriptive names
val appIcon = R.drawable.my_icon
val screenBackground = Color.White
<ImageView android:src="@drawable/icon" />
<ImageView android:src="@drawable/ic_home" />
6. 构建错误诊断与修复
6.1 常见错误速查
| 错误关键字 | 原因 | 修复方法 |
|---|---|---|
Unresolved reference | 缺少导入或未定义 | 检查导入,确认依赖项 |
Type mismatch | 类型不兼容 | 检查参数类型,添加类型转换 |
Cannot access | 可见性问题 | 检查 public/private/internal |
@Composable invocations | 可组合上下文错误 | 确保调用方也带有 @Composable |
Duplicate class | 依赖冲突 | 使用 ./gradlew dependencies 进行排查 |
AAPT: error | 资源文件错误 | 检查 XML 语法和资源引用 |
6.2 修复最佳实践
- 先阅读完整的错误消息:定位文件和行号
- 检查最近的更改:问题通常出在最新修改中
- 清理并构建:
./gradlew clean assembleDebug - 检查依赖项版本:版本冲突是常见原因
- 必要时刷新依赖项:清除缓存并重新构建
6.3 调试命令
# Clean and build
./gradlew clean assembleDebug
# View dependency tree (investigate conflicts)
./gradlew :app:dependencies
# View detailed errors
./gradlew assembleDebug --stacktrace
# Refresh dependencies
./gradlew --refresh-dependencies
7. Material Design 3 指南
检查 Android UI 文件是否符合 Material Design 3 指南和 Android 最佳实践。
设计理念
M3 核心原则
| 原则 | 描述 |
|---|---|
| 个性化 | 根据用户偏好和壁纸动态调整颜色 |
| 自适应 | 适配所有屏幕尺寸和设备形态 |
| 富有表现力 | 通过鲜明的颜色和字体展现个性 |
| 无障碍 | 为所有用户提供包容性的设计 |
M3 Expressive(最新版本)
最新演进通过以下方式打造以情感驱动的 UX:
- 鲜活、动态的颜色
- 直观的运动物理效果
- 自适应组件
- 灵活的字体排版
- 对比鲜明的形状(新增 35 种形状选项)
应用风格选择
关键决策:根据应用类别和目标受众匹配视觉风格。
| 应用类别 | 视觉风格 | 主要特点 |
|---|---|---|
| 实用工具/工具 | 极简主义 | 简洁、高效、中性色彩 |
| 金融/银行 | 专业可信 | 保守的配色,注重安全 |
| 健康/身心健康 | 平和自然 | 柔和的颜色、有机形状 |
| 儿童(3–5 岁) | 简单有趣 | 明亮的颜色、大尺寸触控目标(56dp+) |
| 儿童(6–12 岁) | 趣味十足、引人入胜 | 鲜艳的色彩、游戏化反馈 |
| 社交/娱乐 | 富有表现力 | 品牌驱动、丰富的手势交互 |
| 生产力 | 简洁、专注 | 极简、高对比度 |
| 电子商务 | 以转化为导向 | 清晰的 CTA、便于快速浏览 |
详细的风格配置请参阅设计风格指南。
快速参考:关键规范
颜色对比度要求
| 元素 | 最低比率 |
|---|---|
| 正文文本 | 4.5:1 |
| 大号文本(18sp+) | 3:1 |
| UI 组件 | 3:1 |
触控目标
| 类型 | 尺寸 |
|---|---|
| 最小值 | 48 × 48dp |
| 建议值(主要操作) | 56 × 56dp |
| 儿童应用 | 56dp+ |
| 目标之间的间距 | 至少 8dp |
8dp 网格系统
| 令牌 | 值 | 用途 |
|---|---|---|
| xs | 4dp | 图标内边距 |
| sm | 8dp | 紧凑间距 |
| md | 16dp | 默认内边距 |
| lg | 24dp | 区块间距 |
| xl | 32dp | 大间隔 |
| xxl | 48dp | 屏幕边距 |
字体排版比例(摘要)
| 类别 | 尺寸 |
|---|---|
| Display | 57sp、45sp、36sp |
| Headline | 32sp、28sp、24sp |
| Title | 22sp、16sp、14sp |
| Body | 16sp、14sp、12sp |
| Label | 14sp、12sp、11sp |
动画时长
| 类型 | 时长 |
|---|---|
| 微动画(波纹) | 50-100ms |
| 短动画(简单) | 100-200ms |
| 中等动画(展开/收起) | 200-300ms |
| 长动画(复杂) | 300-500ms |
组件尺寸
| 组件 | 高度 | 最小宽度 |
|---|---|---|
| 按钮 | 40dp | 64dp |
| FAB | 56dp | 56dp |
| 文本字段 | 56dp | 280dp |
| 应用栏 | 64dp | - |
| 底部导航栏 | 80dp | - |
反模式(必须避免)
UI 反模式
- 底部导航项超过 5 个
- 同一屏幕上有多个 FAB
- 触控目标小于 48dp
- 间距不一致(不是 8dp 的倍数)
- 不支持深色主题
- 未检查对比度的彩色背景文字
性能反模式
- 启动时间超过 2 秒且没有进度指示器
- 帧率低于 60 FPS(每帧超过 16ms)
- 崩溃率超过 1.09%(Google Play 阈值)
- ANR 率超过 0.47%(Google Play 阈值)
无障碍反模式
- 交互元素缺少 contentDescription
- 标签中包含元素类型(例如,用“保存”代替“保存按钮”)
- 儿童应用中使用复杂手势
- 面向不识字用户的应用使用纯文本按钮
检查清单
- 符合 8dp 间距网格
- 触控目标至少为 48dp
- 正确使用字体排版比例
- 符合颜色对比度要求(文本为 4.5:1+)
- 支持深色主题
- 所有交互元素均设置 contentDescription
- 启动时间小于 2 秒,或显示进度
- 视觉风格与应用类别相符
设计参考
| 主题 | 参考资料 |
|---|---|
| 颜色、字体排版、间距、形状 | 视觉设计 |
| 动画与过渡 | 动态效果系统 |
| 无障碍指南 | 无障碍 |
| 大屏幕与折叠屏设备 | 自适应屏幕 |
| Android Vitals 与性能 | 性能与稳定性 |
| 隐私与安全 | 隐私与安全 |
| 音频、视频、通知 | 功能要求 |
| 按类别划分的应用风格 | 设计风格指南 |
8. 测试
注意:只有在用户明确要求测试时,才添加测试依赖。
经过充分测试的 Android 应用采用分层测试:使用快速本地单元测试验证逻辑,使用插桩测试验证 UI 和集成,并使用 Gradle Managed Devices 在任何机器(包括 CI)上可重复地运行模拟器。
8.1 测试依赖项
添加测试依赖项前,请检查项目现有版本,以避免冲突:
- 检查
gradle/libs.versions.toml——如果存在,请按项目的版本目录风格添加测试依赖项 - 检查现有
build.gradle.kts中是否已固定依赖项版本 - 根据下表匹配版本系列
版本对齐规则:
| 测试依赖项 | 必须与之对齐 | 检查方法 |
|---|---|---|
kotlinx-coroutines-test | 项目的 kotlinx-coroutines-core 版本 | 在构建文件或版本目录中搜索 kotlinx-coroutines |
compose-ui-test-junit4 | 项目的 Compose BOM 或 compose-compiler | 在构建文件中搜索 compose-bom 或 compose.compiler |
espresso-* | 所有 Espresso 构件必须使用相同版本 | 在构建文件中搜索 espresso |
androidx.test:runner、rules、ext:junit | 应使用兼容的 AndroidX Test 版本 | 在构建文件中搜索 androidx.test |
mockk | 必须支持项目的 Kotlin 版本 | 检查根目录 build.gradle.kts 或版本目录中的 kotlin 版本 |
依赖项参考——仅添加所需的分组:
dependencies {
// --- Local unit tests (src/test/) ---
testImplementation("junit:junit:<version>") // 4.13.2+
testImplementation("org.robolectric:robolectric:<version>") // 4.16.1+
testImplementation("io.mockk:mockk:<version>") // match Kotlin version
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:<version>") // match coroutines-core
testImplementation("androidx.arch.core:core-testing:<version>") // InstantTaskExecutorRule for LiveData
testImplementation("app.cash.turbine:turbine:<version>") // Flow/StateFlow testing
// --- Instrumentation tests (src/androidTest/) ---
androidTestImplementation("androidx.test.ext:junit:<version>")
androidTestImplementation("androidx.test:runner:<version>")
androidTestImplementation("androidx.test:rules:<version>")
androidTestImplementation("androidx.test.espresso:espresso-core:<version>")
androidTestImplementation("androidx.test.espresso:espresso-contrib:<version>") // RecyclerView, Drawer
androidTestImplementation("androidx.test.espresso:espresso-intents:<version>") // Intent verification
androidTestImplementation("androidx.test.espresso:espresso-idling-resource:<version>")
androidTestImplementation("androidx.test.uiautomator:uiautomator:<version>")
// --- Compose UI tests (only if project uses Compose) ---
androidTestImplementation("androidx.compose.ui:ui-test-junit4") // version from Compose BOM
debugImplementation("androidx.compose.ui:ui-test-manifest") // required for createComposeRule
}
注意:如果项目使用 Compose BOM,则
ui-test-junit4和ui-test-manifest无需指定显式版本——由 BOM 管理。
在 android 块中启用 Robolectric 资源支持:
android {
testOptions {
unitTests.isIncludeAndroidResources = true // required for Robolectric
}
}
8.2 分层测试
| 层级 | 位置 | 运行环境 | 速度 | 用途 |
|---|---|---|---|---|
| 单元测试(JUnit) | src/test/ | JVM | ~毫秒 | ViewModel、仓库、映射器、验证器 |
| 单元测试 + Robolectric | src/test/ | JVM + 模拟 Android | ~100 毫秒 | 需要 Context、资源、SharedPrefs 的代码 |
| Compose UI(本地) | src/test/ | JVM + Robolectric | ~100 毫秒 | 可组合项渲染与交互 |
| Espresso | src/androidTest/ | 设备/模拟器 | ~秒 | 基于 View 的 UI 流程、Intents、数据库集成 |
| Compose UI(设备) | src/androidTest/ | 设备/模拟器 | ~秒 | 使用真实渲染的完整 Compose UI 流程 |
| UI Automator | src/androidTest/ | 设备/模拟器 | ~秒 | 系统对话框、通知、多应用场景 |
| 托管设备 | src/androidTest/ | Gradle 管理的 AVD | ~分钟(首次运行) | CI、跨 API 级别的矩阵测试 |
请参阅 测试,了解详细示例、代码模式和 Gradle Managed Device 配置。
8.3 测试命令
# Local unit tests (fast, no emulator)
./gradlew test # all modules
./gradlew :app:testDebugUnitTest # app module, debug variant
# Single test class
./gradlew :app:testDebugUnitTest --tests "com.example.myapp.CounterViewModelTest"
# Instrumentation tests (requires device or managed device)
./gradlew connectedDebugAndroidTest # on connected device
./gradlew pixel6Api34DebugAndroidTest # on managed device
# Both together
./gradlew test connectedDebugAndroidTest
# Test with coverage report (JaCoCo)
./gradlew testDebugUnitTest jacocoTestReport