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

0 阅读32分钟

AI越来越火了,也出现了越来越多的新词,什么MCP、skill等等。我们今天就来手写一个skill。

那到底什么是 Skill?简单理解,Skill 就是一个专属智能小助手,专门帮我们自动化处理各类重复、繁琐的工作,我们只需一键启动,剩下的执行流程全部交给它。

举个通俗的例子:就像玩游戏升级,每天要打 BOSS、做日常任务刷等级。以前需要我们手动一步步操作,而有了 Skill 之后,你只需要开启指令,它就会自动帮你完成打怪、做任务、升级全套流程。

1. 背景

我们公司项目一共有三套网络框架,分别是RetrofitUtils、RetrofitUtils2、RetrofitManager 其中RetrofitUtils、RetrofitUtils2都是创建Service单例,唯一的区别是BaseUrl不一样

演进总览

代数网络库名称语言诞生时间RxJava版本架构模式主要解决问题
第一代RetrofitUtilsJava2017RxJava 2单例 + 静态方法基础网络封装、统一拦截器
第二代RetrofitUtils2Java业务演进RxJava 2单例 + 静态方法支持新 Gateway 域名
第三代basicLibrary RetrofitManagerKotlin2021RxJava 2策略模式 + 闭包注入模块化、策略可扩展
第四代lib_network RetrofitManagerKotlin2026RxJava 3Hilt 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 自动转为空字符串 ""
  • 生产环境禁用代理:防止抓包

类结构

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

AppConfig.BASE_URL  // 旧业务域名:https://qa2-api.at-our.com/atourlife/

使用示例

1. 基础用法 - 获取 Service 实例
// 获取 Service 实例(缓存机制,多次调用只创建一次)
LoginService service = RetrofitUtils.create(LoginService.class);

// 发起请求
service.userlogin(username, password)
    .compose(IoMainTransformer.ioMain())
    .subscribe(response -> {
        // 成功回调
    }, throwable -> {
        // 失败回调
    });
2. 带加载框的请求
Disposable subscribe = RetrofitUtils.create(PersonService.class)
    .getUserInfo()
    .compose(ProgressTransformer.progressDialog(this))
    .subscribe(user -> {
        // 更新 UI
    }, throwable -> {
        // 错误处理
    });
3. 自定义 BaseUrl
// 使用非默认域名创建 Service
HotelService service = RetrofitUtils.create(HotelService.class, "https://custom-api.at-our.com/");
4. 在 Activity 中使用(旧代码)
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 的差异

对比项RetrofitUtilsRetrofitUtils2
默认 BaseUrlAppConfig.BASE_URLAppConfig.BASE_NEW_API_URL
代码实现基本一致几乎完全复制,仅改默认域名
Service 缓存独立缓存 Map独立缓存 Map
拦截器配置相同相同

默认 BaseUrl

AppConfig.BASE_NEW_API_URL  // 新 Gateway 域名:https://qa0-gateway.com/api/

使用示例

1. 新 Gateway 接口调用
// 调用新 Gateway 上的接口(注意:用 RetrofitUtils2,不是 RetrofitUtils!)
SceneRecommendService service = RetrofitUtils2.create(SceneRecommendService.class);

service.getSceneList()
    .compose(IoMainTransformer.ioMain())
    .subscribe(scenes -> {
        // 处理场景列表
    });
2. 在 ViewModel 中使用
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 使用

类结构

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 使用(最常用)
// 本质上还是用 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 使用
class BlueToothViewModel : ViewModel() {
    
    // 新 Gateway 接口,内部调用 RetrofitUtils2
    private val api = RetrofitManager.createService(BlueService::class.java) {
        RetrofitUtils2.create(it)
    }
}
3. 使用内置策略(不推荐)
// 需要先设置 BaseUrl
RetrofitManager.setBaseUrl("https://api.com/")
val service = RetrofitManager.createService(MyService::class.java)
4. 统一请求封装
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 空安全:完整类型安全支持

类结构

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

NetworkConstants.BASE_URL         // 旧业务:https://api.com/atourlife/
NetworkConstants.BASE_NEW_API_URL // 新 Gateway:https://gateway.com/api/

使用示例

前提准备:ViewModel 使用 Hilt 注入
// 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)
@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)
@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
// 特殊场景:调用第三方或测试环境接口
val customService = retrofitManager.create(
    ThirdPartyService::class.java, 
    "https://third-party-api.com/"
)
4. 在 Fragment 中使用
@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
语言JavaJavaKotlinKotlin
RxJava 版本RxJava 2RxJava 2RxJava 2RxJava 3
单例模式静态单例静态单例伴生对象Hilt @Singleton DI
Service 缓存有(全局 Map)有(全局 Map)有(按 BaseUrl 分组缓存)
OkHttpClient每次都创建每次都创建策略内创建全局单例 lazy
Gson 实例每次都创建每次都创建策略内创建全局单例 lazy
多 BaseUrl 支持是(但有 bug,会覆盖)否(只有默认域名)是(完美支持,独立缓存)
默认域名BASE_URLBASE_NEW_API_URL需手动设置两个域名都支持
依赖注入不支持不支持不支持原生支持 Hilt
模块化强耦合 app强耦合 app独立库独立 lib_network 模块
空安全Kotlin 空安全Kotlin 空安全
代码复用与 Utils2 重复与 Utils 重复策略模式复用高度复用
适用场景遗留旧代码遗留新 Gateway过渡阶段推荐,新项目

关键技术差异说明

1. 多 BaseUrl 实现差异

image.png

2. 依赖注入 vs 静态方法
特性静态方法 (前 3 代)Hilt DI (第 4 代)
可测试性差(难以 Mock)好(构造函数注入,易 Mock)
依赖关系隐藏在方法内部显式声明在构造函数
生命周期全局 JVM 单例Hilt 管理的单例
初始化时机首次调用时懒加载Application 创建时

迁移路径

从旧代码迁移到 lib_network 的步骤

步骤 1:添加依赖(模块 build.gradle)
implementation project(':lib_network')
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
步骤 2:ViewModel 改造
// 改造前(旧方式)
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 改造
// 改造前
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:简单迁移
// 改造前
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:双域名并行
// 改造前
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 的迁移修改

// 迁移前
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 编写学习成本中等规模标准化迁移

各方案能力矩阵对比

image.png

为什么选择 Skill 方案?

1. Skill 的本质:可版本化的 "专家提示词 + 工作流程"

与普通的 AI 对话不同,Skill 是一种结构化、可版本化、可测试的自动化工作流:

普通 AI 对话 = 临时提示词 + 随机输出
手写 Skill = 标准化提示词 + 固定工作流程 + 可验证输出 + 版本控制
特性普通 AI 对话手写 Skill
提示词临时写、不一致固化在 SKILL.md 中
工作流程随机探索固定步骤
可复现性极低极高
版本控制无法Git 管理
团队共享困难一键共享
错误修复重新提示更新 Skill

2. Skill 方案的 7 大核心优势

✅ 优势1:可版本控制,持续迭代
# Skill 文件可以像代码一样管理
git add .claude/skills/network-migration/SKILL.md
git commit -m "feat: 支持协程转换逻辑"
git push
  • 每次 Skill 更新都有记录
  • 可以回滚到历史版本
  • 多人协作修改 Skill
✅ 优势2:迁移质量标准化

Skill 中定义了统一的迁移规则,所有开发者使用同一个 Skill 迁移,保证:

  • 注解添加不会遗漏
  • 域名选择不会出错
  • 代码风格保持一致
  • 错误处理模式统一
✅ 优势3:开箱即用,零学习成本
# 任何开发者都可以一键使用,不需要理解迁移细节
/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. 迁移规则的精确表达需求

网络迁移的规则非常精确,需要逐行定义:

### 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 结构,我们遵循这个结构:

---
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 后,每个开发者不需要理解完整的迁移细节

以前:开发者需要理解 → RxJava、协程、Hilt、域名映射、包名、所有转换规则
现在:开发者只需要 → /network-migration [文件名]

3. 可验证、可审计

Skill 迁移的每一步都是透明、可验证的:

  • 迁移前:Skill 读取文件
  • 迁移中:Skill 按固定规则修改
  • 迁移后:可以 diff 检查修改是否正确

如果发现错误,只需要更新 Skill,重新迁移即可。

4. 资产复用价值

这个 Skill 不仅用于这次网络库迁移,还可以:

  • 作为未来架构升级的模板
  • 提取通用模式,创建其他迁移 Skill
  • 作为新员工的学习材料

技术架构设计决策

设计原则:3 个核心决定

决策1:单个文件迁移 vs 批量迁移

选择:单个文件迁移

理由:

  • ✅ 风险可控:错了只影响一个文件
  • ✅ 可测试:迁完一个测一个
  • ✅ 用户体验好:用户可以按自己的节奏迁移
  • ❌ 批量迁移容易出问题,难以排查
决策2:全自动 vs 人机协作

选择:人机协作

Skill 流程设计:

1. 用户指定文件名
2. Skill 定位并读取文件
3. Skill 自动执行迁移
4. 用户检查 diff,确认或手动调整
5. 用户继续下一个文件

理由:

  • 代码迁移总有边界情况
  • 开发者最终把关,保证质量
  • 自动处理 90% 常规情况,人工处理 10% 特殊情况
决策3:硬编码规则 vs 自学习 AI

选择:硬编码精确规则

理由:

  • 迁移规则明确,不需要学习
  • 精确规则比模糊的 AI 更可靠
  • 可预测,可验证

network-migration Skill 架构

flowchart LR
    subgraph "输入层"
        A[用户命令<br/>/network-migration HomePageViewModel]
    end
    
    subgraph "Skill 核心层"
        B[文件定位器<br/>按类名找文件]
        C[文件类型判断<br/>ViewModel/Activity/Fragment]
        D[迁移规则引擎<br/>30+ 条转换规则]
        E[边界情况处理器<br/>特殊场景处理]
    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 人天

投资回报率

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 提出需求

/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 输出的初始版本(核心骨架)

---
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 这种封装好的回调方式,需要转换成协程。

旧代码示例:

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()
            }
        }
    })

提出优化需求

优化 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 中新增章节:

### 转换 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):

public interface FirstPageService {
    Observable<ApiModel<HomePageBean>> homeResource(@Body HomeResourceRequest request);
}

旧接口(Kotlin):

interface FirstPageService {
    fun homeResource(@Body request: HomeResourceRequest): Observable<ApiModel<HomePageBean>>
}

提出优化需求

提示词:很好,但是现在有个问题,很多请求接口的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 接口迁移章节:

### 第六步: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):

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);
}

提出优化需求

优化三: 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 更新内容

新增完整的迁移流程章节:

### 第三步:检测 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

提出优化需求

优化四: Java 如何使用 hilt 的 ViewModel

这里还有一个问题,如果我的Activity是Java,并且是继承BaseActivity,那么内部的ViewModel就不能通过new的方式创建,而是将BaseActivity修改成BaseHiltActivity,并且在BaseHiltActivity内部通过by viewModels()创建ViewModel,并提供get方法,而Activity修改mViewModel = get方式,如 mViewModel = getHotelMapViewModel();

Skill 更新内容

新增 Java Activity 适配章节:

### 第四步: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 获取方式

// 修改前(直接 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 可能被几十个页面引用
  • 全部修改完之前会出现编译错误
  • 无法做到渐进式迁移

结论:需要增量迁移方案!

提出优化需求

优化五:增量迁移方式

现在有个问题,Service接口直接转成kotlin,这样会有个问题,依赖特别多或者耦合特别高的时候,会涉及特别多页面。

所以更好的方式其实是新建Service类,名字为New+原来的名字,修改正确kotlin方式代码,并且替换要修改类的接口为新的。

Skill 更新内容

更新 Service 迁移章节,改为增量方式:

### 第六步: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 创建方式 1RetrofitUtils.create → retrofitManager.create
API 创建方式 2RetrofitUtils2.create → retrofitManager.createNewApi
API 创建方式 3createService 闭包 → 直接调用
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 文件已输出到:

.claude/skills/network-migration/SKILL.md

文件内容包含:

  • 1 个核心目标说明
  • 1 个快速开始指南
  • 1 个迁移规则速查表(11 条规则)
  • 7 个详细的迁移步骤
  • 1 个验证清单(20+ 检查项)

经验总结

1. Skill 创建的迭代模型

初始需求 → 测试迁移 → 发现问题 → 优化 Skill → 再次测试 → ... → 最终成果
     ↓          ↓          ↓          ↓
  核心骨架    第一个文件  边界情况   完善规则

关键:不要期望一次写完美的 Skill,要在实战中迭代!

2. 手写 Skill 的 5 个关键步骤

步骤说明产出物
① 场景分析明确需要解决什么问题,有多少种场景场景清单
② 规则提炼从具体示例中提炼通用的转换规则规则速查表
③ 模式识别识别代码中的常见模式(如 4 种 API 创建方式)转换模式
④ 边界处理考虑异常情况、混合语言、历史技术债等边界处理方案
⑤ 实战调优用真实文件测试,发现问题不断优化成熟可用的 Skill

3. 本次 Skill 创建的 3 个关键决策

决策为什么这么做效果
单个文件迁移批量迁移风险高,难以调试风险可控,渐进式
增量 Service 迁移直接修改原文件会导致大量编译错误迁移过程始终可编译
Lambda 回调而非 LiveData最小化业务逻辑改动降低出错概率

4. skill-creator 与手写的关系

skill-creator = 提供方法论 + 结构模板 + 最佳实践
    ↓
手写 Skill = 填充业务知识 + 细化转换规则 + 处理边界情况
    ↓
最终成果 = 结构化 + 可复用 + 覆盖业务场景的完整 Skill

5. 一些小小的建议

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

结语:

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