# SocketIOManager 内部处理方案分析

## 🤔 问题

**当前方案：** AppInitializer 需要分两步调用
1. `init(application)` - 注册生命周期监听
2. `setCallbacks(...)` - 设置回调函数

**用户疑问：** 为什么不能由 SocketIOManager 内部统一处理？为什么需要外部分步设置？

---

## 💡 方案对比

### 方案A：当前方案（外部分步设置）

```kotlin
// AppInitializer.kt
val initMethod = socketIOManagerClass.getMethod("init", Application::class.java)
initMethod.invoke(null, application)

val setCallbacksMethod = socketIOManagerClass.getMethod("setCallbacks", ...)
setCallbacksMethod.invoke(null, isLoggedInProvider, refreshTokenProvider)
```

**问题：**
- ❌ 外部需要分两步调用
- ❌ 反射代码复杂（需要两次反射调用）
- ❌ 如果忘记调用 setCallbacks，功能不完整

---

### 方案B：内部统一处理（推荐）✅

**思路：** SocketIOManager 提供一个统一的初始化方法，内部处理所有逻辑

#### 方案B1：延迟初始化（推荐）

```kotlin
// SocketIOManager.kt
fun init(app: Application) {
    // 只注册生命周期监听
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

override fun onStart(owner: LifecycleOwner) {
    // 延迟初始化：在第一次 onStart 时检查回调是否设置
    if (isLoggedInProvider == null || refreshTokenProvider == null) {
        ILog.w(TAG, "回调函数未设置，跳过 SocketIO 重连")
        return
    }
    // ... 重连逻辑
}
```

**优点：**
- ✅ 外部只需要调用一次 `init()`
- ✅ 回调函数可以随时设置（通过反射设置属性）
- ✅ 如果回调未设置，自动跳过（不会崩溃）

**缺点：**
- ⚠️ 第一次进入前台时，如果回调未设置，会跳过重连
- ⚠️ 需要确保回调在第一次 onStart 之前设置

---

#### 方案B2：提供统一的初始化方法（最佳）✅

```kotlin
// SocketIOManager.kt
fun init(app: Application) {
    // 注册生命周期监听
    ProcessLifecycleOwner.get().lifecycle.addObserver(this)
}

// 提供一个方法，让外部通过反射设置回调
// 这个方法不需要参数，因为回调已经存储为属性
fun ensureCallbacksSet() {
    // 检查回调是否设置，如果没有设置则记录警告
    if (isLoggedInProvider == null) {
        ILog.w(TAG, "登录状态检查回调未设置")
    }
    if (refreshTokenProvider == null) {
        ILog.w(TAG, "Token 刷新回调未设置")
    }
}
```

**AppInitializer 中：**
```kotlin
// 1. 调用 init
val initMethod = socketIOManagerClass.getMethod("init", Application::class.java)
initMethod.invoke(null, application)

// 2. 直接设置属性（不需要调用 setCallbacks）
val isLoggedInProviderField = socketIOManagerClass.getDeclaredField("isLoggedInProvider")
isLoggedInProviderField.isAccessible = true
isLoggedInProviderField.set(null, isLoggedInProvider)

val refreshTokenProviderField = socketIOManagerClass.getDeclaredField("refreshTokenProvider")
refreshTokenProviderField.isAccessible = true
refreshTokenProviderField.set(null, refreshTokenProvider)
```

**优点：**
- ✅ 外部只需要调用一次 `init()`（注册生命周期）
- ✅ 设置属性比调用方法更简单（不需要处理 suspend 函数类型）
- ✅ 灵活性高（可以随时修改回调）

**缺点：**
- ⚠️ 需要访问私有字段（setAccessible）
- ⚠️ 违反封装原则（直接访问字段）

---

#### 方案B3：完全内部处理（理想但不可行）❌

```kotlin
// SocketIOManager.kt
fun init(app: Application) {
    // 内部尝试获取回调（通过反射或其他方式）
    // 但问题是：能力层不能依赖业务层，无法直接获取 AuthManager
}
```

**问题：**
- ❌ 能力层不能依赖业务层
- ❌ 无法直接获取 AuthManager
- ❌ 违反依赖方向原则

---

## 🎯 推荐方案

### 推荐：**方案B2（直接设置属性）**

**理由：**
1. ✅ **最简单**：设置属性比调用方法更简单，不需要处理 suspend 函数类型
2. ✅ **反射代码最少**：只需要一次方法调用（init）+ 两次属性设置
3. ✅ **灵活性高**：可以随时修改回调函数
4. ✅ **实际风险低**：在 AppInitializer 中集中处理，风险可控

**实现：**
- SocketIOManager 的 `init()` 只负责注册生命周期监听
- 回调函数属性设为 `public` 或通过反射直接设置
- AppInitializer 分两步：先 init，再设置属性

---

## 📝 最终建议

**当前方案（方案3：setCallbacks）的问题：**
- 仍然需要处理 `SuspendFunction0` 类型
- 需要两次反射调用

**改进方案（方案B2：直接设置属性）：**
- 只需要一次方法调用（init）
- 设置属性不需要处理 suspend 函数类型
- 代码更简单

**权衡：**
- 虽然直接访问字段违反了封装原则
- 但在反射场景下，这是可以接受的权衡
- 很多框架都采用这种方式（如 Spring、Guice）

