AI越来越火了,也出现了越来越多的新词,什么MCP、skill等等。我们今天就来手写一个skill。
那到底什么是 Skill?简单理解,Skill 就是一个专属智能小助手,专门帮我们自动化处理各类重复、繁琐的工作,我们只需一键启动,剩下的执行流程全部交给它。
举个通俗的例子:就像玩游戏升级,每天要打 BOSS、做日常任务刷等级。以前需要我们手动一步步操作,而有了 Skill 之后,你只需要开启指令,它就会自动帮你完成打怪、做任务、升级全套流程。
1. 背景
我们公司项目一共有三套网络框架,分别是RetrofitUtils、RetrofitUtils2、RetrofitManager 其中RetrofitUtils、RetrofitUtils2都是创建Service单例,唯一的区别是BaseUrl不一样
演进总览
| 代数 | 网络库名称 | 语言 | 诞生时间 | RxJava版本 | 架构模式 | 主要解决问题 |
|---|---|---|---|---|---|---|
| 第一代 | RetrofitUtils | Java | 2017 | RxJava 2 | 单例 + 静态方法 | 基础网络封装、统一拦截器 |
| 第二代 | RetrofitUtils2 | Java | 业务演进 | RxJava 2 | 单例 + 静态方法 | 支持新 Gateway 域名 |
| 第三代 | basicLibrary RetrofitManager | Kotlin | 2021 | RxJava 2 | 策略模式 + 闭包注入 | 模块化、策略可扩展 |
| 第四代 | lib_network RetrofitManager | Kotlin | 2026 | RxJava 3 | Hilt DI + 多 BaseUrl 缓存 | 依赖注入、多模块共享、类型安全 |
第一代:RetrofitUtils (Java, 2017)
设计背景
项目早期基于 Retrofit + OkHttp 搭建网络层,是最原始的网络封装。诞生于 2017 年,采用 Java 语言编写,配合 RxJava 2 进行异步流式处理。
核心特性
- 单例模式:全局唯一 Retrofit 实例
- Service 缓存 :
ConcurrentHashMap<Class, Object>缓存已创建的 Service - 多 BaseUrl 支持:支持动态切换域名
- 统一拦截器链:参数注入、头部注入、响应拦截、日志、Sentry 监控
- Gson 空值适配 :String 类型 null 自动转为空字符串
"" - 生产环境禁用代理:防止抓包
类结构
java
public class RetrofitUtils {
private static Retrofit retrofit = null;
private static final Map<Class, Object> services = new ConcurrentHashMap<>();
// 默认 BaseUrl
public static Retrofit getInstance() { ... }
// 自定义 BaseUrl
public static Retrofit getInstance(String baseUrl) { ... }
// 创建 Service(默认域名)
public static <T> T create(final Class<T> service) { ... }
// 创建 Service(自定义域名)
public static <T> T create(final Class<T> service, String baseUrl) { ... }
// 重置缓存(域名切换时使用)
public static void changeApiBaseUrl() { ... }
}
默认 BaseUrl
java
AppConfig.BASE_URL // 旧业务域名:https://qa2-api.at-our.com/atourlife/
使用示例
1. 基础用法 - 获取 Service 实例
java
// 获取 Service 实例(缓存机制,多次调用只创建一次)
LoginService service = RetrofitUtils.create(LoginService.class);
// 发起请求
service.userlogin(username, password)
.compose(IoMainTransformer.ioMain())
.subscribe(response -> {
// 成功回调
}, throwable -> {
// 失败回调
});
2. 带加载框的请求
java
Disposable subscribe = RetrofitUtils.create(PersonService.class)
.getUserInfo()
.compose(ProgressTransformer.progressDialog(this))
.subscribe(user -> {
// 更新 UI
}, throwable -> {
// 错误处理
});
3. 自定义 BaseUrl
java
// 使用非默认域名创建 Service
HotelService service = RetrofitUtils.create(HotelService.class, "https://custom-api.at-our.com/");
4. 在 Activity 中使用(旧代码)
java
public class MyFreeShootActivity extends BaseActivity {
private void loadData() {
Disposable subscribe = RetrofitUtils.create(FreeShootService.class)
.getMyFreeShootList()
.compose(IoMainTransformer.ioMain(this)) // 绑定生命周期,防止泄漏
.compose(ProgressTransformer.progressDialog(this))
.subscribe(list -> {
mAdapter.setData(list);
}, throwable -> {
ToastUtils.showToast("加载失败");
});
}
}
第二代:RetrofitUtils2 (Java, 新版 Gateway)
设计背景
随着后端架构演进,引入了新版 Gateway 网关 ,需要独立的域名配置。为了保持向后兼容,不影响旧接口调用,新增了 RetrofitUtils2 专门对接新 Gateway。
与 RetrofitUtils 的差异
| 对比项 | RetrofitUtils | RetrofitUtils2 |
|---|---|---|
| 默认 BaseUrl | AppConfig.BASE_URL |
AppConfig.BASE_NEW_API_URL |
| 代码实现 | 基本一致 | 几乎完全复制,仅改默认域名 |
| Service 缓存 | 独立缓存 Map | 独立缓存 Map |
| 拦截器配置 | 相同 | 相同 |
默认 BaseUrl
java
AppConfig.BASE_NEW_API_URL // 新 Gateway 域名:https://qa0-gateway.com/api/
使用示例
1. 新 Gateway 接口调用
java
// 调用新 Gateway 上的接口(注意:用 RetrofitUtils2,不是 RetrofitUtils!)
SceneRecommendService service = RetrofitUtils2.create(SceneRecommendService.class);
service.getSceneList()
.compose(IoMainTransformer.ioMain())
.subscribe(scenes -> {
// 处理场景列表
});
2. 在 ViewModel 中使用
kotlin
class NewFillViewModel : ViewModel() {
fun submitOrder() {
val disposable = RetrofitUtils2.create(OrderService::class.java)
.submitOrder(orderRequest)
.compose(IoMainTransformer.ioMain())
.subscribe({ result ->
// 提交成功
}, { error ->
// 错误处理
})
}
}
设计缺陷
- 代码重复:RetrofitUtils2 几乎完全复制了 RetrofitUtils 的代码,仅修改默认 BaseUrl
- 维护成本高:拦截器、Gson 配置、超时设置等需要在两个地方同步修改
- 容易混淆:开发者容易搞混哪个 Service 应该用哪个 Utils 创建
第三代:basicLibrary RetrofitManager (Kotlin, 2021)
设计背景
项目逐步 Kotlin 化,同时需要跨模块共享网络基础能力 。引入了 basicLibrary 第三方基础库,其中 RetrofitManager 采用策略模式设计,支持灵活的 Service 创建方式。
核心特性
- Kotlin 语言:完全 Kotlin 实现,支持空安全
- 策略模式 :
IRetrofitStrategy可自定义 Retrofit 构建策略 - 闭包注入:支持通过 Lambda 表达式注入 Service 创建逻辑
- 兼容旧代码:可以配合 RetrofitUtils/RetrofitUtils2 使用
类结构
kotlin
class RetrofitManager {
companion object {
// 策略模式:可替换 Retrofit 创建逻辑
private var mStrategy: IRetrofitStrategy = CommonRetrofitStrategy()
// 方式1:闭包注入(最常用)
fun <T> createService(service: Class<T>, block: (service: Class<T>) -> T): T {
return block(service)
}
// 方式2:默认策略 + 设置的 BaseUrl
fun <T> createService(service: Class<T>): T { ... }
// 方式3:默认策略 + 自定义 BaseUrl
fun <T> createService(service: Class<T>, baseUrl: String): T { ... }
// RxJava 请求封装(带生命周期)
fun <T> createData(observable: Observable<T>, retrofitData: AbstractRetrofitData<T>): Disposable { ... }
}
}
使用示例
1. 配合 RetrofitUtils 使用(最常用)
kotlin
// 本质上还是用 RetrofitUtils 创建,只是包了一层 RetrofitManager
class UserLoginViewModel : ViewModel() {
private val api = RetrofitManager.createService(LoginService::class.java) {
RetrofitUtils.create(it) // 闭包中调用旧的 RetrofitUtils
}
fun login(username: String, password: String) {
api.userlogin(username, password)
.compose(IoMainTransformer.ioMain())
.subscribe({ response ->
// 登录成功
}, { error ->
// 登录失败
})
}
}
2. 配合 RetrofitUtils2 使用
kotlin
class BlueToothViewModel : ViewModel() {
// 新 Gateway 接口,内部调用 RetrofitUtils2
private val api = RetrofitManager.createService(BlueService::class.java) {
RetrofitUtils2.create(it)
}
}
3. 使用内置策略(不推荐)
kotlin
// 需要先设置 BaseUrl
RetrofitManager.setBaseUrl("https://api.com/")
val service = RetrofitManager.createService(MyService::class.java)
4. 统一请求封装
kotlin
RetrofitManager.createData(
observable = api.getUserInfo(),
before = { showLoading() },
success = { user -> updateUI(user) },
error = { e -> showError(e) }
)
设计定位与缺陷
定位 :这是一个过渡层封装,不真正创建 Retrofit,只提供策略扩展能力。
主要缺陷:
- 增加调用层级:每次使用都要写闭包,代码冗余
- 未解决根本问题:内部还是依赖 RetrofitUtils/RetrofitUtils2
- 缓存机制失效:没有利用 Service 缓存,每次都重新创建
第四代:lib_network RetrofitManager (Kotlin, 2026)
设计背景
为了解决前三代网络库的设计缺陷,2026 年推出全新网络模块 lib_network,采用现代化 Android 架构:
- Hilt 依赖注入:替代硬编码的单例模式
- RxJava 3:升级异步框架
- 多 BaseUrl 缓存:支持多个 Retrofit 实例并存
- 独立模块化:可被所有业务模块依赖共享
核心特性
- Hilt 依赖注入 :
@Singleton全局单例,通过构造函数注入 - 多 BaseUrl 独立缓存 :
ConcurrentHashMap<String, Retrofit>每个域名对应独立 Retrofit - 全局 OkHttpClient/Gson 单例:只创建一次,资源复用
- 三种 Service 创建方式:默认域名、新 Gateway 域名、自定义域名
- Kotlin 空安全:完整类型安全支持
类结构
kotlin
class RetrofitManager(val application: Application) {
// 多 BaseUrl -> Retrofit 缓存映射
private val retrofitCache = ConcurrentHashMap<String, Retrofit>()
// 全局单例(只创建一次)
private val gson: Gson by lazy { createGson() }
private val okHttpClient: OkHttpClient by lazy { createOkHttpClient(application) }
// ==================== 三种 Service 创建方式 ====================
// 1. 默认旧业务域名:BASE_URL
fun <T> create(service: Class<T>): T {
return getDefaultRetrofit().create(service)
}
// 2. 新 Gateway 域名:BASE_NEW_API_URL
fun <T> createNewApi(service: Class<T>): T {
return getNewApiRetrofit().create(service)
}
// 3. 自定义域名
fun <T> create(service: Class<T>, baseUrl: String): T {
return getRetrofit(baseUrl).create(service)
}
}
// Hilt 提供单例
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Singleton
@Provides
fun provideRetrofitManager(application: Application): RetrofitManager {
return RetrofitManager(application)
}
}
默认 BaseUrl
kotlin
NetworkConstants.BASE_URL // 旧业务:https://api.com/atourlife/
NetworkConstants.BASE_NEW_API_URL // 新 Gateway:https://gateway.com/api/
使用示例
前提准备:ViewModel 使用 Hilt 注入
kotlin
// 1. ViewModel 添加 @HiltViewModel 注解
@HiltViewModel
class HomePageViewModel @Inject constructor(
private val retrofitManager: RetrofitManager // 2. 构造函数注入
) : ViewModel() {
// 3. 使用时直接调用 create/createNewApi
private val api = retrofitManager.create(FirstPageService::class.java)
private val apiUser = retrofitManager.createNewApi(NewFirstPageService::class.java)
}
// 4. Activity/Fragment 添加 @AndroidEntryPoint 注解
@AndroidEntryPoint
class HomePageActivity : AppCompatActivity() {
private val viewModel: HomePageViewModel by viewModels()
}
1. 旧业务接口(BASE_URL)
kotlin
@HiltViewModel
class HotelOrderDetailViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
// 旧业务 Service,使用 create()
private val api = retrofitManager.create(HotelOrderService::class.java)
fun loadOrderDetail(orderId: String) {
viewModelScope.launch {
api.getOrderDetail(orderId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ detail ->
// 处理订单详情
}, { error ->
// 错误处理
})
}
}
}
2. 新 Gateway 接口(BASE_NEW_API_URL)
kotlin
@HiltViewModel
class JourneyHelperViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
// 新 Gateway Service,使用 createNewApi()
private val api2: NewOrderService = retrofitManager.createNewApi(NewOrderService::class.java)
private val apiJourneyService = retrofitManager.createNewApi(JourneyService::class.java)
}
3. 自定义 BaseUrl
kotlin
// 特殊场景:调用第三方或测试环境接口
val customService = retrofitManager.create(
ThirdPartyService::class.java,
"https://third-party-api.com/"
)
4. 在 Fragment 中使用
kotlin
@AndroidEntryPoint
class MineFragment : Fragment() {
private val viewModel: MineViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.loadUserInfo()
viewModel.userInfo.observe(viewLifecycleOwner) { user ->
// 更新 UI
}
}
}
@HiltViewModel
class MineViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
private val api = retrofitManager.create(PersonService::class.java)
private val _userInfo = MutableLiveData<User>()
val userInfo: LiveData<User> = _userInfo
fun loadUserInfo() {
api.getUserInfo()
.compose(IoMainTransformer.ioMain())
.subscribe({ user ->
_userInfo.value = user
}, {
// 错误处理
})
}
}
四代网络库对比总表
| 对比维度 | 第一代 RetrofitUtils | 第二代 RetrofitUtils2 | 第三代 basicLibrary RetrofitManager | 第四代 lib_network RetrofitManager |
|---|---|---|---|---|
| 语言 | Java | Java | Kotlin | Kotlin |
| RxJava 版本 | RxJava 2 | RxJava 2 | RxJava 2 | RxJava 3 |
| 单例模式 | 静态单例 | 静态单例 | 伴生对象 | Hilt @Singleton DI |
| Service 缓存 | 有(全局 Map) | 有(全局 Map) | 无 | 有(按 BaseUrl 分组缓存) |
| OkHttpClient | 每次都创建 | 每次都创建 | 策略内创建 | 全局单例 lazy |
| Gson 实例 | 每次都创建 | 每次都创建 | 策略内创建 | 全局单例 lazy |
| 多 BaseUrl 支持 | 是(但有 bug,会覆盖) | 否(只有默认域名) | 是 | 是(完美支持,独立缓存) |
| 默认域名 | BASE_URL |
BASE_NEW_API_URL |
需手动设置 | 两个域名都支持 |
| 依赖注入 | 不支持 | 不支持 | 不支持 | 原生支持 Hilt |
| 模块化 | 强耦合 app | 强耦合 app | 独立库 | 独立 lib_network 模块 |
| 空安全 | 无 | 无 | Kotlin 空安全 | Kotlin 空安全 |
| 代码复用 | 与 Utils2 重复 | 与 Utils 重复 | 策略模式复用 | 高度复用 |
| 适用场景 | 遗留旧代码 | 遗留新 Gateway | 过渡阶段 | ✅ 推荐,新项目 |
关键技术差异说明
1. 多 BaseUrl 实现差异

2. 依赖注入 vs 静态方法
| 特性 | 静态方法 (前 3 代) | Hilt DI (第 4 代) |
|---|---|---|
| 可测试性 | 差(难以 Mock) | 好(构造函数注入,易 Mock) |
| 依赖关系 | 隐藏在方法内部 | 显式声明在构造函数 |
| 生命周期 | 全局 JVM 单例 | Hilt 管理的单例 |
| 初始化时机 | 首次调用时懒加载 | Application 创建时 |
迁移路径
从旧代码迁移到 lib_network 的步骤
步骤 1:添加依赖(模块 build.gradle)
groovy
implementation project(':lib_network')
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
步骤 2:ViewModel 改造
kotlin
// 改造前(旧方式)
class OldViewModel : ViewModel() {
private val api = RetrofitUtils.create(MyService::class.java)
private val api2 = RetrofitUtils2.create(NewService::class.java)
}
// 改造后(新方式)
@HiltViewModel // 1. 添加注解
class NewViewModel @Inject constructor(
private val retrofitManager: RetrofitManager // 2. 构造函数注入
) : ViewModel() {
private val api = retrofitManager.create(MyService::class.java) // 3. 替换调用
private val api2 = retrofitManager.createNewApi(NewService::class.java)
}
步骤 3:Activity/Fragment 改造
kotlin
// 改造前
class OldActivity : AppCompatActivity() {
private val viewModel = ViewModelProvider(this).get(OldViewModel::class.java)
}
// 改造后
@AndroidEntryPoint // 1. 添加注解
class NewActivity : AppCompatActivity() {
private val viewModel: NewViewModel by viewModels() // 2. 使用 by viewModels()
}
迁移对照表
| 旧代码 | 新代码 |
|---|---|
RetrofitUtils.create(X::class.java) |
retrofitManager.create(X::class.java) |
RetrofitUtils2.create(X::class.java) |
retrofitManager.createNewApi(X::class.java) |
RetrofitManager.createService(X::class.java) { RetrofitUtils.create(it) } |
retrofitManager.create(X::class.java) |
RetrofitManager.createService(X::class.java) { RetrofitUtils2.create(X::class.java) } |
retrofitManager.createNewApi(X::class.java) |
迁移示例
示例 1:简单迁移
kotlin
// 改造前
private val api = RetrofitUtils.create(FirstPageService::class.java)
// 改造后
@HiltViewModel
class HomeViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
private val api = retrofitManager.create(FirstPageService::class.java)
}
示例 2:双域名并行
kotlin
// 改造前
private val apiOld = RetrofitUtils.create(OldService::class.java)
private val apiNew = RetrofitUtils2.create(NewService::class.java)
// 改造后
@HiltViewModel
class HybridViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
private val apiOld = retrofitManager.create(OldService::class.java) // 旧域名
private val apiNew = retrofitManager.createNewApi(NewService::class.java) // 新域名
}
基于 skill-creator 手写 network-migration 的技术决策分析
如果每次都是自己去手动替换,那工作量太大了,而且这种重复性工作,难免会有写错迷眼的时候,所以这时候我们就可以自己写个skill,交给AI去做
问题背景:为什么需要自动化迁移?
项目规模评估
我们公司 Android 项目网络库迁移涉及的规模:
| 统计项 | 预估数量 | 说明 |
|---|---|---|
| ViewModel 文件 | 50-100+ | 包含网络调用的 ViewModel |
| Activity/Fragment 文件 | 100-200+ | 可能存在直接网络调用 |
| Service 接口文件 | 50-100+ | 需要从 Observable 转为 suspend |
| 总修改文件数 | 200-400+ | |
| 总代码修改行数 | 5000-10000+ |
手动迁移的痛点
如果完全人工手动迁移,会面临以下问题:
- 重复性劳动:200+ 个文件,每个文件都要做几乎相同的修改模式
- 容易出错 :
- 忘记加
@HiltViewModel注解 - 忘记给 Activity 加
@AndroidEntryPoint RetrofitUtils和RetrofitUtils2搞混,用错域名- import 路径错误
- RxJava 到协程的转换逻辑错误
- 忘记加
- 一致性差:不同开发者的迁移风格不一致
- 效率低下:熟练开发者每天也只能迁移 10-20 个文件
- 难以审查:几百个文件的 PR,Code Review 几乎不可能
示例:一个典型 ViewModel 的迁移修改
kotlin
// 迁移前
class HomePageViewModel : ViewModel() {
private val api = RetrofitUtils.create(FirstPageService::class.java)
private val apiUser = RetrofitUtils2.create(NewFirstPageService::class.java)
fun loadData() {
RetrofitManager.createData(api.getHomeData(),
object : ApiStatus<HomeBean>() {
override fun success(t: HomeBean) { ... }
override fun error(e: Exception) { ... }
})
}
}
// 迁移后(需要 5 处修改)
@HiltViewModel // 1. 添加注解
class HomePageViewModel @Inject constructor( // 2. 构造函数注入
private val retrofitManager: RetrofitManager // 3. 注入依赖
) : ViewModel() {
private val api = retrofitManager.create(FirstPageService::class.java) // 4. 替换调用
private val apiUser = retrofitManager.createNewApi(NewFirstPageService::class.java)
fun loadData() {
viewModelScope.launch { // 5. 转换为协程
try {
val result = withContext(Dispatchers.IO) { api.getHomeData() }
// success 逻辑
} catch (e: Exception) {
// error 逻辑
}
}
}
}
关键发现 :每个 ViewModel 的迁移都遵循高度相似、可形式化描述的修改模式------这正是自动化工具的最佳应用场景。
可选方案对比
在决定采用 "skill-creator + 手写 Skill" 方案之前,我们评估了以下 5 种方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 方案1:完全手动迁移 | 无需工具开发成本、灵活度最高 | 慢、易错、一致性差 | 少于 10 个文件的小规模修改 |
| 方案2:正则替换脚本 (sed/awk) | 速度极快、批量处理 | 只能处理简单文本替换,无法处理语法级修改 | 纯字符串替换场景 |
| 方案3:AST 转换工具 (KSP/IDE 插件) | 精确语法级修改 | 开发成本极高(几人周)、学习曲线陡峭 | 大型团队长期维护项目 |
| 方案4:通用 AI 对话 | 零成本、灵活 | 输出不稳定、需要大量提示词、不可复现、无法标准化 | 一次性探索性任务 |
| 方案5:手写 Skill + Claude Code | ⭐ 可控性强、可复现、可版本化、开发成本适中 | 需要一定的 Skill 编写学习成本 | ✅ 中等规模标准化迁移 |
各方案能力矩阵对比
为什么选择 Skill 方案?
1. Skill 的本质:可版本化的 "专家提示词 + 工作流程"
与普通的 AI 对话不同,Skill 是一种结构化、可版本化、可测试的自动化工作流:
ini
普通 AI 对话 = 临时提示词 + 随机输出
手写 Skill = 标准化提示词 + 固定工作流程 + 可验证输出 + 版本控制
| 特性 | 普通 AI 对话 | 手写 Skill |
|---|---|---|
| 提示词 | 临时写、不一致 | 固化在 SKILL.md 中 |
| 工作流程 | 随机探索 | 固定步骤 |
| 可复现性 | 极低 | 极高 |
| 版本控制 | 无法 | Git 管理 |
| 团队共享 | 困难 | 一键共享 |
| 错误修复 | 重新提示 | 更新 Skill |
2. Skill 方案的 7 大核心优势
✅ 优势1:可版本控制,持续迭代
bash
# Skill 文件可以像代码一样管理
git add .claude/skills/network-migration/SKILL.md
git commit -m "feat: 支持协程转换逻辑"
git push
- 每次 Skill 更新都有记录
- 可以回滚到历史版本
- 多人协作修改 Skill
✅ 优势2:迁移质量标准化
Skill 中定义了统一的迁移规则,所有开发者使用同一个 Skill 迁移,保证:
- 注解添加不会遗漏
- 域名选择不会出错
- 代码风格保持一致
- 错误处理模式统一
✅ 优势3:开箱即用,零学习成本
bash
# 任何开发者都可以一键使用,不需要理解迁移细节
/network-migration HomePageViewModel
/network-migration OrderDetailActivity
✅ 优势4:渐进式迁移,风险可控
- 逐个文件迁移:不需要一次性改完所有文件
- 随时暂停/继续:今天迁 10 个,明天迁 10 个
- 可测试:迁完一个测试一个,发现问题更新 Skill
✅ 优势5:知识沉淀,避免流失
Skill 本质上是把网络库迁移的专家知识固化下来:
- 哪个 Service 对应哪个域名
- Hilt 注解怎么加
- RxJava 到协程怎么转
- 各种边界情况怎么处理
这些知识不会因为人员变动而丢失。
✅ 优势6:成本适中,ROI 最高
| 投入项 | 预估工时 |
|---|---|
| Skill 编写与调试 | 1-2 人天 |
| 实际迁移 300 个文件 | 1-2 人天(使用 Skill) |
| 总工时 | 2-4 人天 |
对比手动迁移:
- 手动迁移 300 个文件:10-20 人天
- Skill 方案节省:8-16 人天
✅ 优势7:可扩展能力强
Skill 可以随着迁移过程持续进化:
- 发现新的迁移模式 → 更新 Skill
- 遇到边界情况 → 添加特殊处理规则
- 需要新的转换逻辑 → 扩展 Skill 能力
为什么选择手写而非完全依赖 skill-creator?
skill-creator 是一个指导如何创建 Skill 的元 Skill ,它提供方法论和最佳实践,但我们选择基于其理念手写,而非完全自动生成,原因如下:
1. 网络迁移的业务特殊性
network-migration 不是一个通用 Skill,它包含大量业务特定知识:
| 业务特定知识 | 说明 |
|---|---|
| 域名映射规则 | RetrofitUtils → create(),RetrofitUtils2 → createNewApi() |
| 包名约定 | com.atour.atourlife.lib_network.manager.RetrofitManager |
| 协程转换模式 | RetrofitManager.createData → viewModelScope.launch 的精确转换 |
| Activity-Fragment-ViewModel 关联关系 | 如何找到对应的 ViewModel |
这些业务知识无法通过通用的 skill-creator 自动生成,必须由熟悉项目的开发者手写。
2. 迁移规则的精确表达需求
网络迁移的规则非常精确,需要逐行定义:
markdown
### API 创建替换
| 旧代码 | 新代码 |
|--------|--------|
| `RetrofitUtils.create(...)` | `retrofitManager.create(...)` |
| `RetrofitUtils2.create(...)` | `retrofitManager.createNewApi(...)` |
| `RetrofitManager.createService(X::class.java) { RetrofitUtils.create(it) }` | `retrofitManager.create(X::class.java)` |
这种结构化、表格化的精确规则,需要开发者仔细斟酌每一条,确保 Claude 能准确理解。
3. 调试与迭代的需求
Skill 编写是一个迭代调试的过程:
- 写初步的迁移规则
- 测试迁移一个文件
- 发现 Claude 理解错误
- 修改 Skill,细化规则
- 重复直到完美
这个过程需要开发者的主动参与,skill-creator 只能提供方法论,无法替代调试过程。
4. 避免过度抽象的陷阱
如果完全依赖通用的 skill-creator,可能会:
- 过度设计,做了很多不需要的功能
- 抽象层次太高,实际使用时不精准
- 忽略项目的具体情况
手写 Skill 可以保持极简、精准,只做项目真正需要的事情。
skill-creator 如何赋能手写 Skill
虽然我们选择手写,但 skill-creator 仍然提供了重要的方法论指导:
1. Skill 结构模板
skill-creator 定义了标准的 Skill 结构,我们遵循这个结构:
markdown
---
name: network-migration
description: 一句话描述 Skill 用途
---
# Skill 标题
## 核心目标
## 快速开始
## 迁移规则速查表
## 工作流程
## 边界情况处理
2. 提示词最佳实践
skill-creator 总结的经验:
- 规则表格化:用表格清晰列出转换规则
- 示例驱动:每个规则配迁移前后的代码示例
- 分步执行:将复杂任务拆分为多个步骤
- 明确输出格式:告诉 Claude 应该输出什么
3. 边界情况处理方法论
skill-creator 指导我们如何思考边界情况:
- 极端情况是什么?
- 失败时如何优雅降级?
- 如何验证迁移结果?
skill-creator 与手写 Skill 的关系
skill-creator 提供"道"(方法论),手写提供"术"(具体规则),两者结合产生最佳效果。
手写 network-migration 的核心价值
1. 它是一个"迁移专家",而非简单工具
network-migration Skill 本质上是把网络库迁移所需的所有知识打包成一个可执行的工具:
| 知识类型 | 包含内容 |
|---|---|
| 架构知识 | Hilt 注入模式、ViewModel 架构、协程最佳实践 |
| 业务知识 | 域名映射、包名约定、Service 归属 |
| 语法知识 | Kotlin 语法、注解位置、import 路径 |
| 模式知识 | 20+ 种常见代码模式的转换 |
2. 降低团队认知负担
使用 Skill 后,每个开发者不需要理解完整的迁移细节:
bash
以前:开发者需要理解 → RxJava、协程、Hilt、域名映射、包名、所有转换规则
现在:开发者只需要 → /network-migration [文件名]
3. 可验证、可审计
Skill 迁移的每一步都是透明、可验证的:
- 迁移前:Skill 读取文件
- 迁移中:Skill 按固定规则修改
- 迁移后:可以 diff 检查修改是否正确
如果发现错误,只需要更新 Skill,重新迁移即可。
4. 资产复用价值
这个 Skill 不仅用于这次网络库迁移,还可以:
- 作为未来架构升级的模板
- 提取通用模式,创建其他迁移 Skill
- 作为新员工的学习材料
技术架构设计决策
设计原则:3 个核心决定
决策1:单个文件迁移 vs 批量迁移
选择:单个文件迁移
理由:
- ✅ 风险可控:错了只影响一个文件
- ✅ 可测试:迁完一个测一个
- ✅ 用户体验好:用户可以按自己的节奏迁移
- ❌ 批量迁移容易出问题,难以排查
决策2:全自动 vs 人机协作
选择:人机协作
Skill 流程设计:
markdown
1. 用户指定文件名
2. Skill 定位并读取文件
3. Skill 自动执行迁移
4. 用户检查 diff,确认或手动调整
5. 用户继续下一个文件
理由:
- 代码迁移总有边界情况
- 开发者最终把关,保证质量
- 自动处理 90% 常规情况,人工处理 10% 特殊情况
决策3:硬编码规则 vs 自学习 AI
选择:硬编码精确规则
理由:
- 迁移规则明确,不需要学习
- 精确规则比模糊的 AI 更可靠
- 可预测,可验证
network-migration Skill 架构
/network-migration HomePageViewModel] end subgraph "Skill 核心层" B[文件定位器
按类名找文件] C[文件类型判断
ViewModel/Activity/Fragment] D[迁移规则引擎
30+ 条转换规则] E[边界情况处理器
特殊场景处理] end subgraph "输出层" F[迁移后的 Kotlin 文件] G[修改说明文档] end A --> B B --> C C --> D D --> E E --> F E --> G
ROI 投资回报分析
成本测算
| 投入项 | 工时 | 说明 |
|---|---|---|
| Skill 设计与编写 | 1 人天 | 需求分析、规则梳理 |
| Skill 调试与优化 | 1 人天 | 测试 10+ 个文件,完善规则 |
| 文档编写 | 0.5 人天 | 使用指南、示例 |
| 总开发成本 | 2.5 人天 |
收益测算
| 收益项 | 节省工时 | 说明 |
|---|---|---|
| 300 个文件迁移 | 12 人天 | 手动 15 天,Skill 3 天 |
| 减少错误修复 | 3 人天 | 手动迁移预计 20% 出错率 |
| Code Review 节省 | 2 人天 | 标准化修改,Review 更快 |
| 总收益 | 17 人天 |
投资回报率
erlang
ROI = (收益 - 成本) / 成本 × 100%
= (17 - 2.5) / 2.5 × 100%
= 580%
投入 2.5 人天,节省 14.5 人天,ROI 580%
长期价值
- 后续类似迁移可复用 Skill 模板:+ 5-10 人天
- 团队掌握 Skill 开发能力:长期价值
- 建立自动化迁移的文化:长期价值
结论与建议
核心结论
-
网络库迁移的特性决定了需要自动化工具
- 工作量大、重复度高、容易出错
- 符合自动化工具的应用场景
-
手写 Skill 是当前最佳方案
- 比手动快、比脚本灵活、比 AST 成本低
- 比通用 AI 更可控、可复现、可版本化
-
skill-creator 提供方法论,手写提供业务知识
- 两者结合,而非二选一
- skill-creator 是"指南针",手写是"精确地图"
-
ROI 极高,值得投入
- 投入 2.5 人天,ROI 580%
- 还有长期的资产价值和团队能力提升
Skill 创建过程示例 :网络库迁移
阶段一:初始需求与核心规则
第一步:向 skill-creator 提出需求
typescript
/skill-creator
我想创建一个 skill
场景是这样的, 我目前有个 lib_network 的 Module, 是一个基于 retrofit+hilt 实现的封装库
核心类是:com.atour.atourlife.lib_network.manager.RetrofitManager
目前这个 Module 已经是完成了,但是我已有的项目很多之前都是老的网络封装库:
- RetrofitUtils
- RetrofitUtils2
- RetrofitManager(basicLibrary 中的)
需要做的映射:
1. RetrofitUtils.create → retrofitManager.create
2. RetrofitUtils2.create → retrofitManager.createNewApi
同时需要加入 Hilt 相关注解:
- ViewModel 添加 @HiltViewModel
- 构造函数添加 @Inject constructor(retrofitManager: ...)
- Activity/Fragment 添加 @AndroidEntryPoint
第三种情况:RetrofitManager.createService 的闭包形式
private var api: FirstPageService =
RetrofitManager.createService(FirstPageService::class.java) {
RetrofitUtils.create(it)
}
等价于 → private val apiUser = retrofitManager.create(FirstPageService::class.java)
private val apiUser = RetrofitManager.createService(FirstPageService::class.java) {
RetrofitUtils2.create(FirstPageService::class.java)
}
等价于 → private val apiUser = retrofitManager.createNewApi(FirstPageService::class.java)
我只能想到这些,需要你和我共创这个场景下的 skill,应该如何创建?
我需要你给我创建好,并且将创建好的 Skill 直接放到当前项目下的 .claude/skills/ 目录下
skill-creator 输出的初始版本(核心骨架)
markdown
---
name: network-migration
description: Android项目网络库迁移工具。将旧的网络封装库迁移到新的lib_network模块。
---
# 网络库迁移 Skill
## 核心目标
将项目中旧的网络封装库迁移到新的 `lib_network` 模块,使用 `RetrofitManager` + Hilt 依赖注入架构。
## 迁移规则速查表
### API 创建替换
| 旧代码 | 新代码 |
|--------|--------|
| `RetrofitUtils.create(...)` | `retrofitManager.create(...)` |
| `RetrofitUtils2.create(...)` | `retrofitManager.createNewApi(...)` |
| `RetrofitManager.createService(X::class.java) { RetrofitUtils.create(it) }` | `retrofitManager.create(X::class.java)` |
| `RetrofitManager.createService(X::class.java) { RetrofitUtils2.create(X::class.java) }` | `retrofitManager.createNewApi(X::class.java)` |
## ViewModel 迁移步骤
### 1. 添加 Hilt 注解
```kotlin
// 迁移前
class MyViewModel : ViewModel() { ... }
// 迁移后
@HiltViewModel
class MyViewModel @Inject constructor(
private val retrofitManager: com.atour.atourlife.lib_network.manager.RetrofitManager
) : ViewModel() { ... }
### 2. 转换 API 创建调用
**模式1 - 直接使用 RetrofitUtils:**
```kotlin
// 旧
private val api: MyService = RetrofitUtils.create(MyService::class.java)
// 新
private val api: MyService = retrofitManager.create(MyService::class.java)
**模式2 - 直接使用 RetrofitUtils2:**
```kotlin
// 旧
private val api: MyService = RetrofitUtils2.create(MyService::class.java)
// 新
private val api: MyService = retrofitManager.createNewApi(MyService::class.java)
**模式3 - createService 闭包 + RetrofitUtils:**
```kotlin
// 旧
private var api: FirstPageService =
RetrofitManager.createService(FirstPageService::class.java) {
RetrofitUtils.create(it)
}
// 新
private val apiUser = retrofitManager.create(FirstPageService::class.java)
### 3. Activity/Fragment 添加注解
```kotlin
@AndroidEntryPoint
class MyActivity : AppCompatActivity() { ... }
阶段一成果
- ✅ 完成了 Skill 的核心骨架
- ✅ 定义了三种 API 创建的转换模式
- ✅ 明确了 Hilt 注解添加规则
但是,这只是开始!实际迁移时会发现很多边界情况...
阶段二:优化 1 - RetrofitManager.createData 迁移
发现问题
实际测试迁移时发现:很多 ViewModel 使用了 RetrofitManager.createData 这种封装好的回调方式,需要转换成协程。
旧代码示例:
kotlin
RetrofitManager.createData(
apiUser.homeRecommend(homeHotWordRequest),
object : ApiStatus<ApiModel<HomeRecommendBean>>() {
override fun error(exception: Exception) {
homeRecommendBean.value = null
_homeRecommendBeanState.value = null
hotWordListData.clear()
}
override fun success(t: ApiModel<HomeRecommendBean>) {
if (t.isSuccess) {
homeRecommendBean.value = t.data
val bannerInfos = t.data.bannerInfos
if (!bannerInfos.isNullOrEmpty()) {
val placeholderItem = Recommend(
null, null, null, null, null, -1
)
t.data.recommendList.add(0, placeholderItem)
}
_homeRecommendBeanState.value = t.data
val hotWordList = t.data.hotWordList
if (hotWordList.isNullOrEmpty()) {
hotWordListData.clear()
} else {
hotWordListData.clear()
hotWordListData.addAll(hotWordList)
}
} else {
homeRecommendBean.value = null
_homeRecommendBeanState.value = null
hotWordListData.clear()
}
}
})
提出优化需求
kotlin
优化 1:RetrofitManager.createData 的迁移
还有一些使用的是 RetrofitManager.createData,我们需要将其调用替换成 viewModelScope.launch 方法
替换后应该是:
viewModelScope.launch {
try {
// 切换IO线程执行网络请求
val result = withContext(Dispatchers.IO) {
apiUser.homeRecommend(homeHotWordRequest)
}
// 请求成功,处理业务逻辑
if (result.isSuccess) {
homeRecommendBean.value = result.data
// 推荐更多 - 添加banner占位item
val bannerInfos = result.data?.bannerInfos
if (!bannerInfos.isNullOrEmpty()) {
val placeholderItem = Recommend(
null, null, null, null, null, -1
)
result.data?.recommendList?.add(0, placeholderItem)
}
_homeRecommendBeanState.value = result.data
val hotWordList = result.data?.hotWordList
hotWordListData.clear()
hotWordList?.let { hotWordListData.addAll(it) }
} else {
// 接口返回失败
homeRecommendBean.value = null
_homeRecommendBeanState.value = null
hotWordListData.clear()
}
} catch (exception: Exception) {
// 网络异常/解析异常等,对应原来的 error 回调
homeRecommendBean.value = null
_homeRecommendBeanState.value = null
hotWordListData.clear()
}
}
Skill 更新内容
在 SKILL.md 中新增章节:
markdown
### 转换 RetrofitManager.createData 为协程方式
**旧代码模式:**
```kotlin
RetrofitManager.createData(
apiUser.homeRecommend(homeHotWordRequest),
object : ApiStatus<ApiModel<HomeRecommendBean>>() {
override fun error(exception: Exception) {
// 错误处理逻辑
}
override fun success(t: ApiModel<HomeRecommendBean>) {
// 成功处理逻辑
}
})
**新代码模式:**
```kotlin
viewModelScope.launch {
try {
// 切换IO线程执行网络请求
val result = withContext(Dispatchers.IO) {
apiUser.homeRecommend(homeHotWordRequest)
}
// 请求成功,处理业务逻辑(原success回调内容)
if (result.isSuccess) {
// success 中的逻辑
} else {
// 接口返回失败
}
} catch (exception: Exception) {
// 网络异常/解析异常等(对应原来的 error 回调)
}
}
**关键转换要点:**
1. 移除 `RetrofitManager.createData(...)` 包装
2. 用 `viewModelScope.launch { ... }` 包裹整个逻辑
3. 用 `try { ... } catch { ... }` 替代 `ApiStatus` 回调
4. 网络请求放在 `withContext(Dispatchers.IO) { ... }` 中执行
5. 原 `success` 方法内容移到 try 块中,变量 `t` 改为 `result`
6. 原 `error` 方法内容移到 catch 块中
阶段二成果
- ✅ 支持
RetrofitManager.createData→ 协程转换 - ✅ 明确了 6 个关键转换要点
- ✅ 覆盖了 80% ViewModel 的主要迁移场景
阶段三:优化 2 - Service 接口协程化
发现问题
迁移后发现编译错误!因为新的 create() 方法返回的 Service 需要是 suspend 函数,但原来的 Service 接口返回的是 Observable<T>。
旧接口(Java):
java
public interface FirstPageService {
Observable<ApiModel<HomePageBean>> homeResource(@Body HomeResourceRequest request);
}
旧接口(Kotlin):
kotlin
interface FirstPageService {
fun homeResource(@Body request: HomeResourceRequest): Observable<ApiModel<HomePageBean>>
}
提出优化需求
less
提示词:很好,但是现在有个问题,很多请求接口的Service可能原本是Java接口,需要修改成Kotlin接口,并且函数返回suspend,返回结果Observable需要移除。
有的是Kotlin接口,但是返回的是Observable,也需要移除。如:
fun homeResource(@Body requestBody: HomeResourceRequest): Observable<ApiModel<HomePageBean>>
替换成:
suspend fun homeResource(@Body requestBody: HomeResourceRequest): ApiModel<HomePageBean>
补充:有问题,你这里面只修改了一个Service,我一个类中可能有多个Service,我要求每个Service都需要修改,也就是说你要遍历类中的所有接口Service。
Skill 更新内容
新增 Service 接口迁移章节:
markdown
### 第六步:Service 接口迁移
如果 ViewModel 中使用的 Service 接口还没有转换成协程版本,则按以下方式转换:
#### 转换规则
| 原方法签名 | 新方法签名 |
|-----------|-----------|
| `Observable<ApiModel<T>> method(...)` | `suspend fun method(...): ApiModel<T>` |
| Java 接口方法 | Kotlin suspend 方法 |
**关键转换要点:**
1. 函数添加 `suspend` 关键字
2. 移除外层的 `Observable<>` 包装
3. 保留真正的返回类型(如 `ApiModel<XXX>`)
4. 注解 `@Body`、`@GET`、`@POST` 等保持不变
5. **遍历所有 Service**:一个 ViewModel 可能引用多个 Service 实例(如 `apiUser`、`apiOrder`、`apiHotel` 等),每个都需要检查转换
**转换示例:**
```kotlin
// 迁移前
fun homeResource(@Body requestBody: HomeResourceRequest): Observable<ApiModel<HomePageBean>>
// 迁移后
suspend fun homeResource(@Body requestBody: HomeResourceRequest): ApiModel<HomePageBean>
**多 Service 处理规则:**
- 搜索 ViewModel 中所有形如 `private val apiXxx: XxxService = ...` 的变量
- 逐个检查每个 Service 接口
- 每个 Service 中的所有方法都需要转换为 suspend
阶段三成果
- ✅ 支持 Java/Kotlin Service 接口的协程化转换
- ✅ 明确了多 Service 的处理规则(逐个检查,不漏掉)
- ✅ 解决了迁移后的编译问题
阶段四:优化 3 - Activity/Fragment 网络调用上移
发现问题
迁移了几个 ViewModel 后发现:很多 Activity/Fragment 中直接就做网络调用,根本没有经过 ViewModel!这是历史技术债。
旧代码示例(Java Activity):
java
private void checkAppUpdate() {
Disposable subscribe = RetrofitUtils.create(IndexService.class)
.getUpdatedata()
.compose(IoMainTransformer.create(this))
.subscribe(indexDataApiModel -> {
if (indexDataApiModel.isSuccessful()) {
UpdateBean updateData = indexDataApiModel.getResult();
switch (updateData.category) {
case 1:
if (!PreferenceUtils.getBoolean(Constants.REFUSE_UPDATE, false)) {
new UpdateDialog(this, updateData).show();
}
break;
case 2:
new UpdateDialog(this, updateData).show();
break;
}
}
});
addSubscribe(subscribe);
}
提出优化需求
kotlin
优化三: Fragment/Activity 中网络调用迁移到 ViewModel
有的Activity和Fragment对网络调用,并没有用ViewModel,现在需要迁移到对应的viewModel,需要注意:
ViewModel实现接口调用,结果用回调,而不是赋值。
示例:
ViewModel迁移后是:
fun checkAppUpdate(success: ((UpdateBean)-> Unit)?=null){
viewModelScope.launch {
try {
val result= withContext(Dispatchers.IO){
apiIndex.getUpdatedata()
}
if (result.isSuccessful) {
val updateData: UpdateBean = result.getResult()
success?.invoke(updateData)
}
} catch (e: Exception) {
}
}
}
修改后调用处变成了:
private void checkAppUpdate() {
mFirstPageViewModel.checkAppUpdate(new Function1<UpdateBean, Unit>() {
@Override
public Unit invoke(UpdateBean updateBean) {
switch (updateBean.category) {
case 1:
if (!PreferenceUtils.getBoolean(Constants.REFUSE_UPDATE, false)) {
new UpdateDialog(MainActivity.this, updateBean).show();
}
break;
case 2:
new UpdateDialog(MainActivity.this, updateBean).show();
break;
}
return null;
}
});
}
Skill 更新内容
新增完整的迁移流程章节:
markdown
### 第三步:检测 Activity/Fragment 中的直接网络调用
**在迁移 ViewModel 之前,先扫描 Activity/Fragment 中是否有直接的网络调用:**
```kotlin
// 搜索以下模式:
RetrofitUtils.create
RetrofitUtils2.create
RetrofitManager.createService
RetrofitManager.createData
IoMainTransformer
.subscribe(
.addSubscribe(
#### 3.1 识别对应的 ViewModel
- **情况A**:Activity/Fragment 已经有对应的 ViewModel
- 将网络调用逻辑迁移到已有的 ViewModel 中
- **情况B**:Activity/Fragment 没有 ViewModel
- **询问用户**:是否有对应的 ViewModel?还是需要创建新的?
#### 3.2 将网络调用迁移到 ViewModel(回调方式)
**重要:采用 Lambda 回调方式,而非 LiveData 赋值!**
**新代码(ViewModel 中 - Kotlin 回调方式):**
```kotlin
@HiltViewModel
class HomePageViewModel @Inject constructor(
private val retrofitManager: RetrofitManager
) : ViewModel() {
private val apiIndex = retrofitManager.create(IndexService::class.java)
/**
* 检查App更新
* @param success 成功回调,返回 UpdateBean
*/
fun checkAppUpdate(success: ((UpdateBean) -> Unit)? = null) {
viewModelScope.launch {
try {
val result = withContext(Dispatchers.IO) {
apiIndex.getUpdatedata()
}
if (result.isSuccessful) {
val updateData: UpdateBean = result.getResult()
success?.invoke(updateData)
}
} catch (e: Exception) {
// 异常处理
}
}
}
}
**新代码(Activity/Fragment 中 - Java 调用):**
```java
private void checkAppUpdate() {
mFirstPageViewModel.checkAppUpdate(new Function1<UpdateBean, Unit>() {
@Override
public Unit invoke(UpdateBean updateBean) {
// 原业务逻辑全部保留在这里
switch (updateBean.category) {
case 1:
if (!PreferenceUtils.getBoolean(Constants.REFUSE_UPDATE, false)) {
new UpdateDialog(MainActivity.this, updateBean).show();
}
break;
case 2:
new UpdateDialog(MainActivity.this, updateBean).show();
break;
}
return null;
}
});
}
**回调方式关键要点:**
1. ViewModel 方法参数为 `success: ((T) -> Unit)? = null` 形式的 lambda 回调
2. 回调参数可为 null,使用 `success?.invoke(data)` 调用
3. 业务逻辑全部保留在 Activity/Fragment 中,通过回调执行
4. **不需要创建 LiveData/StateFlow 变量**
5. 方法命名保持原语义(如 `checkAppUpdate`)
阶段四成果
- ✅ 支持检测 Activity/Fragment 中的直接网络调用
- ✅ 支持将网络调用迁移到 ViewModel(Lambda 回调方式)
- ✅ 解决了历史技术债问题
阶段五:优化 4 - Java Activity 的 Hilt 适配
发现问题
迁移后 Java Activity 又报错了!因为:
- Hilt 的
by viewModels()是 Kotlin 语法糖 - Java 代码中不能直接用
- Java Activity 继承的 BaseActivity 也不支持 Hilt
提出优化需求
csharp
优化四: Java 如何使用 hilt 的 ViewModel
这里还有一个问题,如果我的Activity是Java,并且是继承BaseActivity,那么内部的ViewModel就不能通过new的方式创建,而是将BaseActivity修改成BaseHiltActivity,并且在BaseHiltActivity内部通过by viewModels()创建ViewModel,并提供get方法,而Activity修改mViewModel = get方式,如 mViewModel = getHotelMapViewModel();
Skill 更新内容
新增 Java Activity 适配章节:
markdown
### 第四步:Activity / Fragment 添加 Hilt 注解
#### 4.1 Kotlin Activity/Fragment
为使用了注入 ViewModel 的 Kotlin Activity 或 Fragment 添加 `@AndroidEntryPoint` 注解:
```kotlin
@AndroidEntryPoint
class MyActivity : AppCompatActivity() { ... }
#### 4.2 Java Activity(继承 BaseActivity)
**Java Activity 不能直接使用 `@AndroidEntryPoint` + `by viewModels()`,需要通过 BaseHiltActivity 桥接:**
**步骤1:修改继承关系**
```java
// 修改前
public class HotelMapListActivity extends BaseActivity {
private HotelMapViewModel mViewModel;
}
// 修改后
public class HotelMapListActivity extends BaseHiltActivity {
private HotelMapViewModel mViewModel;
}
步骤2:修改 ViewModel 获取方式
java
// 修改前(直接 new 创建)
mViewModel = new HotelMapViewModel();
// 修改后(通过 BaseHiltActivity 的 get 方法获取)
mViewModel = getHotelMapViewModel();
**步骤3:在 BaseHiltActivity 中添加对应 ViewModel**
如果 BaseHiltActivity 中没有该 ViewModel,需要添加:
```kotlin
// 在 BaseHiltActivity.kt 中添加
private val mHotelMapViewModel: HotelMapViewModel by viewModels()
fun getHotelMapViewModel(): HotelMapViewModel {
return mHotelMapViewModel
}
**关键点:**
- Java Activity 继承 BaseHiltActivity(而非直接 BaseActivity)
- BaseHiltActivity 是 Kotlin 类,使用 `by viewModels()` 创建 ViewModel
- BaseHiltActivity 对外提供 `getXxxViewModel()` 方法供 Java 调用
- **不需要给 Java Activity 添加 `@AndroidEntryPoint` 注解**(基类已处理)
阶段五成果
- ✅ 支持 Java Activity 的 Hilt 适配
- ✅ 通过 BaseHiltActivity 桥接方案解决 Java/Kotlin 互操作问题
- ✅ 覆盖了混合语言项目的场景
阶段六:优化 5 - 增量迁移方案
发现问题
直接修改原 Service 文件会有大问题!
- 一个 Service 可能被几十个页面引用
- 全部修改完之前会出现编译错误
- 无法做到渐进式迁移
结论:需要增量迁移方案!
提出优化需求
sql
优化五:增量迁移方式
现在有个问题,Service接口直接转成kotlin,这样会有个问题,依赖特别多或者耦合特别高的时候,会涉及特别多页面。
所以更好的方式其实是新建Service类,名字为New+原来的名字,修改正确kotlin方式代码,并且替换要修改类的接口为新的。
Skill 更新内容
更新 Service 迁移章节,改为增量方式:
markdown
### 第六步:Service 接口迁移(增量迁移方式)
**重要:采用增量迁移方式,不修改原 Service 文件,避免影响其他未迁移的页面!**
如果 ViewModel 中使用的 Service 接口还没有对应的 NewXxxService(Kotlin suspend 版本),则按以下方式创建新的 Service 接口:
#### 6.1 新建 NewXxxService Kotlin 接口
**原文件保持不变**(`FirstPageService.java` 或 `FirstPageService.kt` 不动),在同目录下新建 `NewFirstPageService.kt`:
```kotlin
// 文件名:NewFirstPageService.kt
interface NewFirstPageService {
// 复制原接口中的所有方法,添加 suspend 并移除 Observable<> 包装
suspend fun homeResource(@Body requestBody: HomeResourceRequest): ApiModel<HomePageBean>
suspend fun otherMethod(@Body request: OtherRequest): ApiModel<OtherBean>
}
#### 6.2 更新 ViewModel 中的引用
ViewModel 中修改 Service 的创建,使用新的 NewXxxService:
```kotlin
// 修改前
private val api: FirstPageService = retrofitManager.create(FirstPageService::class.java)
// 修改后
private val api: NewFirstPageService = retrofitManager.create(NewFirstPageService::class.java)
**增量迁移的优势:**
1. ✅ 原 Service 文件保持不变,不影响其他未迁移页面
2. ✅ 可以逐个 ViewModel 迁移,渐进式推进
3. ✅ 迁移过程中项目始终能编译通过
4. ✅ 全部迁移完成后,可以统一删除旧 Service 文件
阶段六成果
- ✅ 实现了增量迁移方案
- ✅ 迁移过程中项目始终可编译
- ✅ 降低了大规模迁移的风险
最终成果:完整的 Skill 输出
经过 6 个阶段的迭代优化,最终产出了一个功能完整、覆盖所有场景的 network-migration Skill:
Skill 功能清单
| 功能模块 | 覆盖场景 |
|---|---|
| ViewModel Hilt 注解 | @HiltViewModel、@Inject、构造函数注入 |
| API 创建方式 1 | RetrofitUtils.create → retrofitManager.create |
| API 创建方式 2 | RetrofitUtils2.create → retrofitManager.createNewApi |
| API 创建方式 3 | createService 闭包 → 直接调用 |
| createData 迁移 | RetrofitManager.createData → viewModelScope.launch + 协程 |
| Service 接口迁移 | Java/Kotlin Observable → Kotlin suspend(NewXxxService 增量方式) |
| Activity/Fragment 注解 | @AndroidEntryPoint |
| 直接网络调用检测 | 扫描 Activity/Fragment 中的网络调用 |
| 网络调用上移 | Activity/Fragment → ViewModel(Lambda 回调方式) |
| Java Activity 适配 | BaseHiltActivity 桥接方案 |
| 多 Service 处理 | 遍历所有 Service 实例,逐个转换 |
| 包名修正 | 自动修正 RetrofitManager 的包名 |
| 导入清理 | 自动清理无用的 RxJava 导入 |
Skill 核心文件
完整的 SKILL.md 文件已输出到:
bash
.claude/skills/network-migration/SKILL.md
文件内容包含:
- 1 个核心目标说明
- 1 个快速开始指南
- 1 个迁移规则速查表(11 条规则)
- 7 个详细的迁移步骤
- 1 个验证清单(20+ 检查项)
经验总结
1. Skill 创建的迭代模型
markdown
初始需求 → 测试迁移 → 发现问题 → 优化 Skill → 再次测试 → ... → 最终成果
↓ ↓ ↓ ↓
核心骨架 第一个文件 边界情况 完善规则
关键:不要期望一次写完美的 Skill,要在实战中迭代!
2. 手写 Skill 的 5 个关键步骤
| 步骤 | 说明 | 产出物 |
|---|---|---|
| ① 场景分析 | 明确需要解决什么问题,有多少种场景 | 场景清单 |
| ② 规则提炼 | 从具体示例中提炼通用的转换规则 | 规则速查表 |
| ③ 模式识别 | 识别代码中的常见模式(如 4 种 API 创建方式) | 转换模式 |
| ④ 边界处理 | 考虑异常情况、混合语言、历史技术债等 | 边界处理方案 |
| ⑤ 实战调优 | 用真实文件测试,发现问题不断优化 | 成熟可用的 Skill |
3. 本次 Skill 创建的 3 个关键决策
| 决策 | 为什么这么做 | 效果 |
|---|---|---|
| 单个文件迁移 | 批量迁移风险高,难以调试 | 风险可控,渐进式 |
| 增量 Service 迁移 | 直接修改原文件会导致大量编译错误 | 迁移过程始终可编译 |
| Lambda 回调而非 LiveData | 最小化业务逻辑改动 | 降低出错概率 |
4. skill-creator 与手写的关系
ini
skill-creator = 提供方法论 + 结构模板 + 最佳实践
↓
手写 Skill = 填充业务知识 + 细化转换规则 + 处理边界情况
↓
最终成果 = 结构化 + 可复用 + 覆盖业务场景的完整 Skill
5. 一些小小的建议
- 不要追求完美:先能跑起来,再逐步优化
- 用真实文件测试:不要纸上谈兵,直接拿项目中的文件试
- 做好版本管理:Skill 文件要进 Git,每次更新有记录
- 写好示例:迁移前后的代码示例是 Skill 的灵魂
- 从简单场景开始:先搞定 80% 常见情况,再处理 20% 边界
结语:
手写 Skill 就像搭积木------先搭核心骨架,再一块一块往上加功能,遇到不合适的就调整。最终你会得到一个完全贴合自己项目需求、高效好用的自动化工具。这就是基于 skill-creator 手写 Skill 的真正价值所在!
