# 回调函数传递方案对比 ## 📋 当前实现(方案1:反射调用 init 方法) ### 实现方式 ```kotlin // AppInitializer.kt val initMethod = socketIOManagerClass.getDeclaredMethod( "init", Application::class.java, kotlin.jvm.functions.Function0::class.java, // isLoggedInProvider kotlin.coroutines.SuspendFunction0::class.java // refreshTokenProvider ) initMethod.invoke(null, application, isLoggedInProvider, refreshTokenProvider) ``` ### 优点 ✅ 1. **初始化逻辑集中**:init 方法同时完成生命周期注册和回调设置 2. **原子性操作**:一次调用完成所有初始化,不会出现部分初始化状态 3. **符合单例模式**:标准的单例初始化模式 4. **类型安全**:回调函数在编译时创建,类型明确 ### 缺点 ❌ 1. **反射调用复杂**:需要处理 `SuspendFunction0` 类型,反射代码复杂 2. **可读性差**:反射代码不够直观 3. **错误处理复杂**:反射异常处理需要更多代码 --- ## 🔄 方案2:分步设置(先 init,再设置属性) ### 实现方式 ```kotlin // SocketIOManager.kt fun init(app: Application) { // 只负责注册生命周期监听 ProcessLifecycleOwner.get().lifecycle.addObserver(this) } // AppInitializer.kt // 1. 先调用 init(注册生命周期) val initMethod = socketIOManagerClass.getMethod("init", Application::class.java) initMethod.invoke(null, application) // 2. 再设置回调属性(直接设置,不需要处理 suspend 函数类型) val isLoggedInProviderField = socketIOManagerClass.getDeclaredField("isLoggedInProvider") isLoggedInProviderField.isAccessible = true isLoggedInProviderField.set(null, isLoggedInProvider) val refreshTokenProviderField = socketIOManagerClass.getDeclaredField("refreshTokenProvider") refreshTokenProviderField.isAccessible = true refreshTokenProviderField.set(null, refreshTokenProvider) ``` ### 优点 ✅ 1. **反射代码简单**:设置属性比调用方法更简单 2. **不需要处理 suspend 函数类型**:直接设置属性,反射不需要知道 suspend 类型 3. **灵活性高**:可以随时修改回调函数 4. **可读性好**:分步操作,逻辑清晰 ### 缺点 ❌ 1. **初始化逻辑分散**:需要两步操作 2. **可能出现部分初始化**:如果第二步失败,可能出现只注册了生命周期但没设置回调的情况 3. **需要访问私有字段**:需要 `setAccessible(true)`,可能违反封装原则 4. **错误处理分散**:需要分别处理 init 和属性设置的异常 --- ## 📊 客观对比 | 维度 | 方案1:反射调用 init | 方案2:分步设置属性 | |------|---------------------|-------------------| | **代码复杂度** | ⭐⭐⭐ 复杂(需要处理 SuspendFunction0) | ⭐⭐ 简单(直接设置属性) | | **可读性** | ⭐⭐ 一般(反射代码不够直观) | ⭐⭐⭐ 好(分步操作清晰) | | **类型安全** | ⭐⭐⭐ 好(编译时类型检查) | ⭐⭐ 一般(运行时设置) | | **原子性** | ⭐⭐⭐ 好(一次调用完成) | ⭐⭐ 一般(分步操作) | | **错误处理** | ⭐⭐ 一般(集中处理) | ⭐⭐ 一般(分散处理) | | **灵活性** | ⭐⭐ 一般(需要重新 init) | ⭐⭐⭐ 好(可随时修改) | | **封装性** | ⭐⭐⭐ 好(通过方法调用) | ⭐⭐ 一般(直接访问字段) | --- ## 🎯 推荐方案 ### 推荐:**方案2(分步设置)** **理由:** 1. ✅ **反射代码更简单**:设置属性比调用方法简单,不需要处理复杂的 suspend 函数类型 2. ✅ **可读性更好**:分步操作逻辑清晰,易于理解和维护 3. ✅ **实际风险低**:虽然可能出现部分初始化,但在 AppInitializer 中集中处理,风险可控 4. ✅ **符合常见模式**:很多框架都是先初始化,再配置回调 **改进建议:** - 在 SocketIOManager 中添加 `setCallbacks()` 方法,通过方法设置而不是直接访问字段 - 这样可以保持封装性,同时简化反射代码 --- ## 💡 改进方案(推荐) ### 方案3:添加 setCallbacks 方法(最佳) ```kotlin // SocketIOManager.kt fun init(app: Application) { // 只负责注册生命周期监听 ProcessLifecycleOwner.get().lifecycle.addObserver(this) } fun setCallbacks( isLoggedInProvider: (() -> Boolean)? = null, refreshTokenProvider: (suspend () -> String?)? = null ) { this.isLoggedInProvider = isLoggedInProvider this.refreshTokenProvider = refreshTokenProvider } // AppInitializer.kt // 1. 先调用 init(注册生命周期) val initMethod = socketIOManagerClass.getMethod("init", Application::class.java) initMethod.invoke(null, application) // 2. 再调用 setCallbacks(设置回调) val setCallbacksMethod = socketIOManagerClass.getMethod( "setCallbacks", kotlin.jvm.functions.Function0::class.java, // isLoggedInProvider kotlin.coroutines.SuspendFunction0::class.java // refreshTokenProvider ) setCallbacksMethod.invoke(null, isLoggedInProvider, refreshTokenProvider) ``` **优势:** - ✅ 保持封装性(通过方法而不是字段) - ✅ 反射代码相对简单(调用方法比设置字段更标准) - ✅ 逻辑清晰(分步操作) - ✅ 类型安全(方法签名明确) **缺点:** - ⚠️ 仍然需要处理 `SuspendFunction0` 类型(但比方案1简单,因为方法签名更清晰) --- ## 📝 结论 **当前方案(方案1)的问题:** - 反射调用 init 方法时,需要处理复杂的 `SuspendFunction0` 类型 - 虽然功能正常,但代码复杂度较高 **推荐改进:** - 采用**方案3(添加 setCallbacks 方法)** - 或者采用**方案2(分步设置属性)**(如果不在意直接访问字段) **最终建议:** - 如果追求代码简洁:使用**方案2(分步设置属性)** - 如果追求封装性:使用**方案3(添加 setCallbacks 方法)**