You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Ngrx与ngx-socket-io的Ionic应用锁屏恢复后崩溃问题求助

问题根因

  1. 10分钟刚好是Socket.IO服务端默认的闲置连接超时断开阈值,锁屏期间应用退到后台,连接被服务端主动断开,但你本地手动维护的socketConnected状态没有同步更新,仍为true,状态不一致导致后续连接/断开操作触发底层异常。
  2. 应用退到后台(pause状态)时没有主动断开Socket连接,WebView中闲置的长连接被系统/服务端回收时会产生未捕获的底层错误。
  3. ngrx的session状态订阅没有销毁逻辑,应用从后台恢复时会重复触发订阅回调,重复调用连接/断开方法导致冲突。
  4. connectSocket方法中,Socket连接还未建立成功就直接发送JOIN_ROOM事件,且没有处理凭证请求失败的异常,异常未捕获直接导致应用崩溃。
  5. 前台恢复(resume状态)时没有重新触发合法的连接逻辑,Socket底层默认自动重连时没有携带有效凭证,导致连接异常闪退。

修复方案

1. 修复WebSocket服务逻辑

去掉手动维护的连接状态,改用Socket原生属性判断,补全异常捕获和事件触发时机:

export class WebsocketService {
  constructor(private socket: Socket,  public http: HttpClient, public apiConfigService: ApiConfigService, public authService: AuthService, public platform: Platform) { 
      this.checkStatus();
  }

  async connectSocket() {
      // 已连接/连接中直接返回,避免重复调用
      if (this.socket.ioSocket.connected || this.socket.ioSocket.connecting) return;
      try {
        const data = await this.socketCredentials();
        if (!data) return;
        this.socket.ioSocket.io.opts.query = { sessionUUID: data.sessionUUID, channelUUID: data.channelUUID };
        // 连接成功后再发送加入房间事件
        this.socket.once('connect', () => {
          this.socket.emit('JOIN_ROOM', {channelID: data.channelUUID});
        });
        this.socket.connect();
      } catch (e) {
        // 捕获凭证请求失败的异常,避免闪退
        console.error('Socket连接失败', e);
      }
  }

  disconnectSocket() {
      // 用原生连接状态判断,避免状态不一致
      if (this.socket.ioSocket.connected) { 
        this.socket.disconnect(); 
      }
  }

  checkStatus() {
      this.socket.on('connect', () => { console.log('SOCKET CONNECTED');  });
      this.socket.on('disconnect', () => { console.log('SOCKET DISCONNECTED'); });
      // 补全socket错误事件捕获,避免未捕获异常闪退
      this.socket.on('error', (e) => { console.error('SOCKET ERROR', e); });
      this.socket.on('connect_error', (e) => { console.error('SOCKET CONNECT ERROR', e); });
  }

  socketCredentials(): Promise<any> {
      return this.http.get('http://localhost:3000/socket/auth').toPromise();
  }
}

2. 修复app.component生命周期和状态监听

补全订阅销毁,后台主动断开,前台恢复主动重连:

import { Subject } from 'rxjs';
import { takeUntil, take } from 'rxjs/operators';

export class AppComponent {
  private destroy$ = new Subject<void>();

  constructor(public store: Store<AppState>, public websocketService: WebsocketService, public appStatusService: AppStatusService, public platform: Platform) { }

  ngOnInit() {
      this.store.select('session').pipe(
        filter(session => session !== null),
        takeUntil(this.destroy$) // 组件销毁时取消订阅,避免内存泄漏和重复触发
      ).subscribe(session => {
          if (session.currentSession.token === null) { 
            this.websocketService.disconnectSocket();  
          } else { 
            this.websocketService.connectSocket();     
          }
      });
      this.initializeApp();
  }

  initializeApp() {
      this.platform.ready().then(() => {
        this.platform.pause.subscribe(e => { 
          this.appStatusService.setStatus(false); 
          // 退到后台主动断开socket,避免闲置被回收产生异常
          this.websocketService.disconnectSocket();
        });
        this.platform.resume.subscribe(e => { 
          this.appStatusService.setStatus(true); 
          // 恢复前台时重新触发连接逻辑
          this.store.select('session').pipe(take(1)).subscribe(session => {
            if (session?.currentSession.token) {
              this.websocketService.connectSocket();
            }
          })
        });
      });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }
}

3. 可选优化Socket配置

把不需要的传输协议去掉,避免polling模式在后台产生异常请求:

const config: SocketIoConfig = { 
  url: 'http://localhost:5000', 
  options: { 
    transports: ["websocket"], // 仅保留websocket模式
    autoConnect: false,
    reconnection: false // 关闭自动重连,改为手动控制重连逻辑
  } 
};

内容的提问来源于stack exchange,提问作者Sergio Mendez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 08:18:03