使用Ngrx与ngx-socket-io的Ionic应用锁屏恢复后崩溃问题求助
问题根因
- 10分钟刚好是Socket.IO服务端默认的闲置连接超时断开阈值,锁屏期间应用退到后台,连接被服务端主动断开,但你本地手动维护的
socketConnected状态没有同步更新,仍为true,状态不一致导致后续连接/断开操作触发底层异常。 - 应用退到后台(pause状态)时没有主动断开Socket连接,WebView中闲置的长连接被系统/服务端回收时会产生未捕获的底层错误。
- ngrx的session状态订阅没有销毁逻辑,应用从后台恢复时会重复触发订阅回调,重复调用连接/断开方法导致冲突。
connectSocket方法中,Socket连接还未建立成功就直接发送JOIN_ROOM事件,且没有处理凭证请求失败的异常,异常未捕获直接导致应用崩溃。- 前台恢复(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
相关产品推荐
相关产品推荐

