import { Context, UIAbility } from '@kit.AbilityKit'; import { ILog, StorageImpl } from '@xdz/base-core'; import { SocketIORepositoryFactory } from '@xdz/capability-socketio'; import { AuthManager } from '../auth/AuthManager'; const TAG = "SocketIOManager"; /** * SocketIO 连接管理器(业务封装层) * * 与 AuthManager、VersionUpdateManager 类似,提供统一的 Socket.IO 封装 * 自动处理连接、重连、Token 刷新等,外部只需要初始化即可 * * 负责自动管理 SocketIO 连接的生命周期: * - App 进入前台时自动重连 * - Token 过期时自动刷新并重连 * - 监听应用生命周期 * * 使用方式: * ```typescript * // 在 EntryAbility.onCreate() 中初始化 * SocketIOManager.init(context, ability) * * // 在 EntryAbility.onForeground() 中调用 * await SocketIOManager.onForeground() * * // 在 EntryAbility.onBackground() 中调用 * SocketIOManager.onBackground() * ``` */ export class SocketIOManager { private static instance: SocketIOManager | null = null; private context: Context | null = null; private initialized: boolean = false; private ability: UIAbility | null = null; private constructor() { // 私有构造函数,单例模式 } /** * 获取单例实例 */ static getInstance(): SocketIOManager { if (!SocketIOManager.instance) { SocketIOManager.instance = new SocketIOManager(); } return SocketIOManager.instance; } /** * 初始化 SocketIO 管理器 * * 需要在 EntryAbility.onCreate() 中调用 * 会自动注册生命周期监听 * * @param context Application Context 实例 * @param ability UIAbility 实例(用于监听生命周期) */ static init(context: Context, ability?: UIAbility): void { const instance = SocketIOManager.getInstance(); if (instance.initialized) { ILog.w(TAG, "SocketIOManager 已初始化,跳过重复初始化"); return; } instance.context = context; instance.ability = ability || null; instance.initialized = true; // 如果提供了 ability,注册生命周期监听 if (ability) { // HarmonyOS 的生命周期监听通过重写 ability 的方法实现 // 这里我们使用一个包装器来监听生命周期 instance.setupLifecycleListener(ability); } ILog.d(TAG, "SocketIOManager 初始化完成(生命周期监听已注册)"); } /** * 设置生命周期监听器 * * 注意:HarmonyOS 的生命周期管理需要通过 Ability 的 onForeground/onBackground 方法 * 这里提供一个辅助方法,需要在 EntryAbility 中手动调用 */ private setupLifecycleListener(ability: UIAbility): void { // HarmonyOS 的生命周期监听需要在 EntryAbility 中手动调用 // 这里只是保存引用,实际的生命周期回调在 EntryAbility 中调用 this.ability = ability; } /** * App 进入前台时调用(需要在 EntryAbility.onForeground() 中手动调用) * * 检查 SocketIO 连接状态,如果断开则自动重连 * 如果连接失败(可能是 Token 过期),会自动刷新 Token 后重连 */ static async onForeground(): Promise { ILog.d(TAG, "App 进入前台,检查 SocketIO 连接状态"); // 检查是否已登录 if (!await AuthManager.isLoggedIn()) { ILog.d(TAG, "用户未登录,跳过 SocketIO 重连"); return; } // 在后台执行重连逻辑(使用 Promise,不依赖 ExecutorManager) Promise.resolve().then(async () => { try { // 直接获取 SocketIORepository(不使用反射) const socketIORepository = SocketIORepositoryFactory.getInstance(); // 检查是否已连接 if (socketIORepository.isConnected()) { ILog.d(TAG, "SocketIO 已连接,无需重连"); return; } // 检查 token 是否存在 const token = await StorageImpl.getInstance().getString("access_token"); if (!token || token === '') { ILog.w(TAG, "Token 为空,无法重连 SocketIO"); return; } // 先尝试连接(不传参数,让它从存储中获取) ILog.d(TAG, "SocketIO 未连接,开始重连..."); const reconnected = await socketIORepository.checkAndReconnect(null, null); if (!reconnected) { // 等待 2 秒,检查连接是否成功 await new Promise((resolve) => { setTimeout(() => { resolve(); }, 2000); }); // 如果连接仍然失败,可能是 Token 过期,尝试刷新 Token 后重连 if (!socketIORepository.isConnected()) { ILog.d(TAG, "连接失败,可能是 Token 过期,尝试刷新 Token 后重连"); const refreshedToken = await AuthManager.refreshTokenIfNeeded(); if (refreshedToken && refreshedToken !== token) { ILog.d(TAG, "Token 已刷新,使用新 Token 重连"); await socketIORepository.checkAndReconnect(null, null); } else { ILog.w(TAG, "Token 刷新失败或未刷新,无法重连"); } } else { ILog.d(TAG, "SocketIO 重连成功"); } } } catch (e) { ILog.e(TAG, "SocketIO 重连失败", e as Error); } }); } /** * App 进入后台时调用(需要在 EntryAbility.onBackground() 中手动调用) * * 注意:通常不断开 SocketIO 连接,因为: * 1. SocketIO 连接是轻量级的 * 2. 用户可能需要在后台接收消息 * 3. 系统会在内存不足时自动清理 */ static onBackground(): void { ILog.d(TAG, "App 进入后台"); // 不断开连接,保持连接以便接收消息 } }