| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- 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<void> {
- 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<void>((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 进入后台");
- // 不断开连接,保持连接以便接收消息
- }
- }
|