基于 Hilt 实现 Android 网络库可插拔替换 Skill

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 缓存 依赖注入、多模块共享、类型安全
timeline title 网络库演进时间线 2017 : RetrofitUtils (Java) : 基础网络封装 : RxJava 2 业务演进 : RetrofitUtils2 (Java) : 支持新 Gateway : 双域名并行 2021 : basicLibrary RetrofitManager (Kotlin) : 策略模式 : 闭包注入 Service 2026 : lib_network RetrofitManager (Kotlin) : Hilt 依赖注入 : RxJava 3 : 多 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 实例并存
  • 独立模块化:可被所有业务模块依赖共享

核心特性

  1. Hilt 依赖注入@Singleton 全局单例,通过构造函数注入
  2. 多 BaseUrl 独立缓存ConcurrentHashMap<String, Retrofit> 每个域名对应独立 Retrofit
  3. 全局 OkHttpClient/Gson 单例:只创建一次,资源复用
  4. 三种 Service 创建方式:默认域名、新 Gateway 域名、自定义域名
  5. 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+
pie title 网络库迁移工作量分布 "ViewModel 迁移" : 40 "Activity/Fragment 迁移" : 30 "Service 接口改造" : 20 "测试与回归" : 10

手动迁移的痛点

如果完全人工手动迁移,会面临以下问题:

  1. 重复性劳动:200+ 个文件,每个文件都要做几乎相同的修改模式
  2. 容易出错
    • 忘记加 @HiltViewModel 注解
    • 忘记给 Activity 加 @AndroidEntryPoint
    • RetrofitUtilsRetrofitUtils2 搞混,用错域名
    • import 路径错误
    • RxJava 到协程的转换逻辑错误
  3. 一致性差:不同开发者的迁移风格不一致
  4. 效率低下:熟练开发者每天也只能迁移 10-20 个文件
  5. 难以审查:几百个文件的 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,它包含大量业务特定知识

业务特定知识 说明
域名映射规则 RetrofitUtilscreate()RetrofitUtils2createNewApi()
包名约定 com.atour.atourlife.lib_network.manager.RetrofitManager
协程转换模式 RetrofitManager.createDataviewModelScope.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 编写是一个迭代调试的过程:

  1. 写初步的迁移规则
  2. 测试迁移一个文件
  3. 发现 Claude 理解错误
  4. 修改 Skill,细化规则
  5. 重复直到完美

这个过程需要开发者的主动参与,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 的关系

graph TD A[skill-creator 方法论] --> B[Skill 结构模板] A --> C[提示词最佳实践] A --> D[边界情况处理框架] E[开发者手写] --> F[业务特定迁移规则] E --> G[精确代码转换模式] E --> H[项目包名与约定] B & C & D & F & G & H --> I[最终 network-migration 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 架构

flowchart LR subgraph "输入层" A[用户命令
/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 开发能力:长期价值
  • 建立自动化迁移的文化:长期价值

结论与建议

核心结论

  1. 网络库迁移的特性决定了需要自动化工具

    • 工作量大、重复度高、容易出错
    • 符合自动化工具的应用场景
  2. 手写 Skill 是当前最佳方案

    • 比手动快、比脚本灵活、比 AST 成本低
    • 比通用 AI 更可控、可复现、可版本化
  3. skill-creator 提供方法论,手写提供业务知识

    • 两者结合,而非二选一
    • skill-creator 是"指南针",手写是"精确地图"
  4. 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. 一些小小的建议

  1. 不要追求完美:先能跑起来,再逐步优化
  2. 用真实文件测试:不要纸上谈兵,直接拿项目中的文件试
  3. 做好版本管理:Skill 文件要进 Git,每次更新有记录
  4. 写好示例:迁移前后的代码示例是 Skill 的灵魂
  5. 从简单场景开始:先搞定 80% 常见情况,再处理 20% 边界

结语:

手写 Skill 就像搭积木------先搭核心骨架,再一块一块往上加功能,遇到不合适的就调整。最终你会得到一个完全贴合自己项目需求、高效好用的自动化工具。这就是基于 skill-creator 手写 Skill 的真正价值所在!

相关推荐
小杍随笔9 小时前
【Tauri 2.x 自定义 WebView2 用户数据目录完全指南】
架构·rust
黄林晴9 小时前
Google I/O 2026 Android开发者速览
android·android studio
hadeas9 小时前
Spring 深入篇:MVC 请求处理 / MyBatis / 注解机制
架构
敖正炀9 小时前
云原生持续交付:GitOps 与渐进式发布
分布式·架构
xwz小王子9 小时前
SkiP:让模仿学习学会“快进“——动作重标记如何在不改架构的情况下削减机器人 15-40% 的执行步数
学习·架构·机器人
靠谱品牌推荐官9 小时前
【架构实战】如何设计一套原生支持 GEO 大模型爬虫语义索引的 HTML5/CSS3 纯净白盒前端架构?
前端·爬虫·架构
jarvisuni9 小时前
《掌门日记》之GPT5.5测评报告!
人工智能·ai编程
枫叶林FYL9 小时前
【自然语言处理 NLP】9.1 检索增强生成高级架构:GraphRAG 与结构化知识检索
人工智能·自然语言处理·架构
DogDaoDao10 小时前
Android 播放器开发:从零构建全功能视频播放器
android·ffmpeg·音视频·播放器·mediacodec·编解码