
📰 科技要闻
• OpenAI 正计划将 Sora 视频生成器整合进 ChatGPT,目前 Sora 作为独立产品存在感较弱,此举或改变多模态 AI 格局。
• 追觅旗下芯际穿越发布"天穹"系列 AI 芯片,已规模化量产并将搭载于泛机器人产品线,端侧 AI 算力竞赛进一步加速。
• eSIM 在海内外手机支持现状引发讨论,国内政策限制与国际漫游需求之间的矛盾日益凸显。
上篇文章发出之后,有朋友问能不能把 android-dev Skill 分享出来。
我想了想,直接给文件不如把内容讲清楚——授人以鱼不如授人以渔。况且每个团队的规范不一样,你直接用我的 Skill,不一定适配你们的项目,但如果你理解了它是怎么设计出来的,就能随时改造成自己的版本。
这篇是进阶版,重点讲两件事:怎么引导 AI 设计出真正有用的 Skill,以及 android-dev Skill 完整的实现细节——不是截图,是可以直接对着写的完整内容。
先想清楚三个问题
我见过一种很常见的错误用法:直接让 AI "帮我写一个 Android 开发的 Skill",对着生成结果点点头就用了。
结果往往是 SKILL.md 里塞满了正确但无用的废话——"使用 Kotlin 编写代码"、"遵循 Android 最佳实践"。这些东西 AI 没有 Skill 也知道,写进去只是浪费上下文窗口,每次触发都要把这堆废话塞进去。
核心原则:Skill 只写 AI「没有这个 Skill 就不知道的东西」。通用知识不需要写,团队规范、项目约束、历史踩坑才值得写。
在让 AI 动手之前,先回答这三个问题:
问题一:你每次和 AI 交互时,反复解释的是什么?
比如我每次让 AI 写 Android 代码,都要说:
• 用 Hilt 做依赖注入,不要用 Koin
• 状态管理用 sealed class + StateFlow,不要用 LiveData
• Repository 只返回 suspend fun 或 Flow,不要直接返回 LiveData
这些就是 Skill 应该写的东西——一次写进去,以后不用再解释。
问题二:AI 在这个领域犯过哪些错误?
我踩过的坑:
• AI 生成的 ViewModel 里用了 GlobalScope.launch——会导致内存泄漏
• Fragment 里的 _binding 没有在 onDestroyView 置空——生命周期经典坑
• 在主线程调 SharedPreferences.commit() 而不是 apply()——ANR 来源
这些错误模式写进 Skill 的反模式章节,让 AI 主动规避。
问题三:哪些内容需要随时间更新?
依赖版本是最典型的——半年前的 Compose BOM 版本今天可能已经落后两个大版本了。这类内容单独放进 references/dependencies.md,更新时不需要动 SKILL.md 主体。
让 AI 帮你写:正确的提问方式
把三个问题的答案组织好,交给 AI 时加上这句关键话:
"请帮我设计 Skill 的文件结构,然后生成 SKILL.md 的内容。记住:SKILL.md 正文要简洁,详细内容放进对应的 references 文件,不要把所有东西都堆在 SKILL.md 里。"
不加这句,AI 会把所有东西往 SKILL.md 里塞,一个文件写到两三千字,每次触发都把这堆内容塞进上下文,极其低效。
android-dev Skill 完整内容
下面是 android-dev Skill 的完整实现,可以直接对着写。
文件结构
android-dev/
├── SKILL.md
└── references/
├── conventions.md
├── crash_patterns.md
└── dependencies.mdSKILL.md
description 踩过一个坑:最开始写"Android 开发相关问题",触发率很低——AI 判断不出"帮我分析这个 ANR 日志"跟 Android 开发有什么关系。把具体触发场景都列出来之后,准确率才明显提升:
---
name: android-dev
description: Android 开发辅助 skill。
当用户提出 Android 开发相关需求
时使用,包括:生成 Kotlin/Java
代码(ViewModel、Repository、
Compose UI、Room、Retrofit 等)、
分析 Crash/ANR 日志、审查
Android 代码质量、解释 Android
系统机制(生命周期、进程管理、
渲染流程)、提供依赖版本建议、
生成 Gradle 配置片段。
---# Android Dev Skill
# updated: 2025-Q4
# stack: Kotlin/Compose/Hilt## 代码生成规范详见 references/conventions.md:- 语言:优先 Kotlin
- 架构:MVVM + Repository
- 异步:suspend + Flow/StateFlow
- DI:Hilt(禁止 Koin)
- 状态:UiState sealed class
- 禁止:GlobalScope、裸 Thread## Crash 分析流程详见 references/crash_patterns.md:
1. 识别异常类型
2. 定位应用包名帧
3. 结合生命周期判断根因
4. 给出最小复现 + 修复方案## 依赖版本当前稳定版见 references/dependencies.mdreferences/conventions.md
告诉 AI 「不要做 X」比「要做 Y」更有效——反模式是具体的,正面规范是抽象的。这个文件的核心是对比示例:
## ViewModel 标准模板@HiltViewModel
class UserViewModel @Inject constructor(
private val repo: UserRepository
) : ViewModel() {private val _state =
MutableStateFlow<UserUiState>(
UserUiState.Loading
)
val state =
_state.asStateFlow()fun loadUser(id: String) {
viewModelScope.launch {
_state.value =
UserUiState.Loading
repo.getUser(id)
.onSuccess { user ->
_state.value =
UserUiState
.Success(user)
}
.onFailure { e ->
_state.value =
UserUiState
.Error(e.message)
}
}
}
}sealed class UserUiState {
object Loading : UserUiState()
data class Success(
val user: User
) : UserUiState()
data class Error(
val msg: String?
) : UserUiState()
}## Repository 标准模板class UserRepository @Inject constructor(
private val remote:
UserRemoteDataSource,
private val local:
UserLocalDataSource
) {
suspend fun getUser(
id: String
): Result<User> =
runCatching {
local.getUser(id)
?: remote.fetchUser(id)
.also {
local.save(it)
}
}
}## Compose UI 模板@Composable
fun UserScreen(
vm: UserViewModel =
hiltViewModel()
) {
val state by
vm.state
.collectAsStateWithLifecycle()when (state) {
is UserUiState.Loading ->
CircularProgressIndicator()
is UserUiState.Success ->
UserContent(
(state as
UserUiState.Success).user
)
is UserUiState.Error ->
ErrorView(
(state as
UserUiState.Error).msg
)
}
}## ❌ 禁止模式// ❌ 内存泄漏
GlobalScope.launch { ... }
// ✅ 替代
viewModelScope.launch { ... }// ❌ Fragment binding 泄漏
// onDestroyView 后未置空
// ✅ 正确写法
private var _binding:
FragmentXxxBinding? = null
private val binding
get() = _binding!!override fun onDestroyView() {
super.onDestroyView()
_binding = null // 必须
}// ❌ 主线程 IO → ANR
prefs.edit().commit()
// ✅
prefs.edit().apply()// ❌ ViewModel 存 Context
class XxxViewModel(
val ctx: Context // 泄漏!
)
// ✅ 用 AndroidViewModel
// 或 SavedStateHandle// ❌ Repository 返回 LiveData
fun getUser(): LiveData<User>
// ✅
suspend fun getUser(): Result<User>
fun userFlow(): Flow<User>references/crash_patterns.md
按异常类型组织,每种类型:特征识别 → 根因 → 修复。
## NPE / NullPointerException特征:`java.lang.NullPointerException`常见根因:
- View binding 在 onDestroyView
后被访问
- getActivity() 在 Fragment
detach 后调用
- Parcelable 字段未处理 null修复:
private var _binding:
FragmentXxxBinding? = null
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}## ANR特征:`ANR in <package>`
+ `Input dispatching timed out`分析步骤:
1. 看 main thread stacktrace
2. 识别阻塞类型:
- IO:SP.commit/File/DB
- 锁:synchronized 死锁
- Binder:跨进程超时修复:
// SP 改 apply
prefs.edit().apply()// DB 操作移到 IO 线程
withContext(Dispatchers.IO) {
db.userDao().insert(user)
}## OOM特征:
`OutOfMemoryError: Failed to allocate`常见根因:
- Bitmap 未回收 / 未采样
- 静态字段持有 Context/View
- RecyclerView 图片未复用Bitmap 安全加载:
val opts =
BitmapFactory.Options().apply {
inJustDecodeBounds = true
BitmapFactory
.decodeFile(path, this)
inSampleSize =
calcSampleSize(
this, reqW, reqH
)
inJustDecodeBounds = false
}
val bmp =
BitmapFactory
.decodeFile(path, opts)## IllegalStateException常见场景:
- Fragment already added:
重复 add 同一 Fragment
- Can not perform after
onSaveInstanceState:
Activity 在后台时 commit
Fragment 事务
- ViewHolder views must not
be attached:RecyclerView
复用问题修复:非关键场景用
commitAllowingStateLoss(),
或在 onResume 中执行事务。references/dependencies.md
## 2025-Q4 稳定版[versions]
compose-bom = "2024.12.01"
lifecycle = "2.8.7"
hilt = "2.53"
room = "2.6.1"
navigation = "2.8.5"
retrofit = "2.11.0"
okhttp = "4.12.0"
coil = "2.7.0"
mockk = "1.13.13"
turbine = "1.2.0"[libraries]
compose-bom = {
group = "androidx.compose",
name = "compose-bom",
version.ref = "compose-bom"
}
hilt-android = {
group = "com.google.dagger",
name = "hilt-android",
version.ref = "hilt"
}
lifecycle-viewmodel = {
group = "androidx.lifecycle",
name = "lifecycle-viewmodel-ktx",
version.ref = "lifecycle"
}
room-ktx = {
group = "androidx.room",
name = "room-ktx",
version.ref = "room"
}
coil = {
group = "io.coil-kt",
name = "coil-compose",
version.ref = "coil"
}迭代:Skill 是需要养的
Skill 不是写完就万事大吉的,它需要持续迭代。我的节奏是这样的:
每次 AI 犯了新错误,立刻补充进 conventions.md 的反模式清单。这是最频繁的更新,也是让 Skill 越来越好的核心机制。
每个季度,更新一次 dependencies.md 的版本号。Compose BOM、Hilt、Room 这些库迭代很快,版本落后半年就可能影响新 API 的使用。
引入新技术栈时,在 conventions.md 里加对应模板,在 SKILL.md 的 description 里追加触发词。比如引入 Paging 3 后,description 加上"分页加载、Paging",正文加一条"Paging 详见 conventions.md"。
还有一个我觉得很有价值的工作流:让 AI 来审查 Skill 本身。把文件贴给 AI,问它:
"读这个 Skill,告诉我哪些内容是冗余的?哪些触发场景没有覆盖到?references 里有没有明显缺失的内容?"
AI 作为 Skill 的"用户",视角和你写 Skill 时不一样,往往能发现你自己看不出来的问题。我第一次做这个审查,AI 发现了两个漏洞:description 里没有提到"Gradle 配置"导致写 build.gradle.kts 时无法触发;conventions.md 里有 Compose 模板,但缺少处理 Dark Theme 和动态颜色的规范。两个漏洞加进去,Skill 的覆盖范围又扩大了一圈。
团队共享:统一 AI 的输出标准
做完这个 Skill 之后,我感受最深的一点不是"AI 帮我写代码更快了",而是:团队里所有人用同一个 Skill,AI 给大家的输出就有了统一标准。
以前 Code Review 里有一大类评论是"这里应该用 Hilt"、"ViewModel 不能存 Context"——规范问题,不是逻辑问题。有了统一的 Skill 之后,AI 生成的代码天然符合规范,Review 可以专注在真正的业务逻辑上。
打包分发一条命令:
python3 package_skill.py \
./android-dev \
./dist
# → dist/android-dev.skill
# 本质是 zip,放进 Git 管理我们把 Skill 文件放进团队 Git 仓库统一管理,成员 pull 之后直接安装,和管理代码依赖一样自然。打包脚本会自动校验 frontmatter 格式和 description 长度,校验不过不让打包,避免有人改坏了 Skill 没发现。
如果你基于这套思路在自己的项目里做了定制改造,欢迎交流——这类东西越迭代越值钱,藏着不如一起完善。