041-网络状态监听与请求重试 041 — 网络状态监听与请求重试弱网环境下的用户体验保障简介移动应用的使用场景遍布地铁、电梯、隧道等弱网环境网络请求随时可能中断。如果应用不做任何处理用户只会看到一个无休止的加载转圈体验极差。MoneyTrack 通过ohos.net.connection的网络状态监听能力结合 ReminderUtil 离线提醒、指数退避重试和离线队列机制构建了一套完整的弱网保障体系实时监听网络状态变化、在断网时给出友好提示、在网络恢复后自动重发离线队列中的请求确保用户的记账操作不因网络波动而丢失。核心知识点1. NetConnect 网络状态监听ohos.net.connection提供了createNetConnection()方法创建网络连接监听器完整的生命周期包括注册监听、事件响应和取消注册import{connection}fromohos.net.connection;// 1. 创建网络连接监听器constnetConnectionconnection.createNetConnection();// 2. 注册事件回调// 网络可用netConnection.on(netAvailable,(){console.info([Network] 网络已恢复);// 触发全局事件自动刷新数据 发送离线队列EventBus.emit(EventConstants.NETWORK_RESTORED);});// 网络断开netConnection.on(netLost,(){console.error([Network] 网络连接丢失);ReminderUtil.showOfflineWarning();});// 网络能力变化如 WiFi → 蜂窝netConnection.on(netCapabilitiesChange,(){console.info([Network] 网络能力变化);// 可在此处重新检测网络质量});// 3. 开始监听register 成功后才会收到回调netConnection.register().then((){console.info([Network] 网络监听已注册);}).catch((err){console.error([Network] 注册失败${JSON.stringify(err)});});// 4. 页面销毁时取消注册避免内存泄漏// onPageHide 或 aboutToDisappear 中调用netConnection.unregister();2. 指数退避重试当请求因网络原因失败时采用指数退避策略进行自动重试——每次重试的等待时间呈指数增长避免对服务端造成冲击asyncfunctionretryWithBackoffT(requestFn:()PromiseT,maxRetries:number3,initialDelay:number1000):PromiseT{letlastError:Error;for(letattempt0;attemptmaxRetries;attempt){try{returnawaitrequestFn();}catch(error){lastErrorerrorasError;// 非网络错误不重试if((errorasany).response){throwerror;// 服务端返回的错误无需重试}if(attemptmaxRetries){// 指数退避1s → 2s → 4sconstdelayinitialDelay*Math.pow(2,attempt);console.info([Network] 第${attempt1}次重试等待${delay}ms);awaitnewPromise((resolve)setTimeout(resolve,delay));}}}throwlastError!;}// 使用示例asyncfunctionfetchBillListWithRetry(){returnretryWithBackoff(()request.get(/api/bill/list),3,1000);}重试次数等待时间累计等待第 1 次1 秒1 秒第 2 次2 秒3 秒第 3 次4 秒7 秒3. 离线队列当检测到网络断开时将发起的请求暂存到离线队列中网络恢复后自动按顺序重发队列中的请求interfaceQueuedRequest{id:string;url:string;method:GET|POST|PUT|DELETE;data?:any;timestamp:number;}classOfflineQueue{privatequeue:QueuedRequest[][];privateisProcessing:booleanfalse;// 入队publicenqueue(request:OmitQueuedRequest,id|timestamp):void{this.queue.push({...request,id:util.generateRandomUUID(false),timestamp:Date.now(),});console.info([OfflineQueue] 请求已加入离线队列队列长度${this.queue.length});}// 网络恢复后处理队列publicasyncprocessQueue():Promisevoid{if(this.isProcessing||this.queue.length0)return;this.isProcessingtrue;console.info([OfflineQueue] 开始处理离线队列共${this.queue.length}个请求);while(this.queue.length0){constrequestthis.queue.shift()!;try{awaitrequest.post(request.url,request.data);console.info([OfflineQueue] 请求成功${request.url});}catch(error){console.error([OfflineQueue] 请求失败${request.url},error);// 重试失败放回队列头部this.queue.unshift(request);break;}}this.isProcessingfalse;}publicgetQueueLength():number{returnthis.queue.length;}}exportconstofflineQueuenewOfflineQueue();4. 请求取消切换页面时取消未完成请求使用 AbortController 在页面离开时取消正在进行的网络请求避免页面已销毁但请求回调仍执行导致的内存泄漏importaxiosfromohos/axios;exportfunctioncreateCancellableRequest(){constcontrollernewAbortController();return{// 发起可取消的请求request:(url:string){returnaxios.get(url,{signal:controller.signal});},// 取消请求cancel:(){controller.abort();console.info([Network] 请求已取消);},};}// ViewModel 中使用classBillViewModel{privatecancellablecreateCancellableRequest();publicasyncloadBillList():Promisevoid{try{constdataawaitthis.cancellable.request(/api/bill/list);// 更新 UI}catch(error){if((errorasany).codeERR_CANCELED){console.info([BillViewModel] 请求已被取消忽略);return;}// 处理其他错误}}// 页面离开时取消publiccleanup():void{this.cancellable.cancel();}}网络状态变化→UI 反馈→自动重连完整流程后端服务网络请求离线队列NetConnection应用页面后端服务网络请求离线队列NetConnection应用页面注册网络监听alt[网络断开][网络恢复]页面销毁register()监听已注册netLost 事件展示离线提示ReminderUtil请求入队暂存enqueue(request)netAvailable 事件隐藏离线提示触发队列处理逐条重发发送请求返回数据更新 UIunregister()最佳实践注册/取消注册成对出现在页面aboutToAppear中register()在aboutToDisappear中unregister()避免内存泄漏和重复注册。指数退避上限设置最大延迟上限如 30 秒防止重试等待时间过长影响用户体验。同时设置最大重试次数3-5 次为宜避免无限重试消耗流量。离线队列幂等性入队的请求应保证幂等重复执行结果一致避免网络恢复后重发导致数据重复。请求取消防泄漏页面切换时务必取消未完成的请求。尤其在列表页快速滚动的场景不及时取消请求可能导致页面卡顿和状态错乱。区分错误类型网络超时可重试但 4xx/5xx 服务端错误不应重试。在响应拦截器中做好错误分类。推荐参考文档ohos.net.connection 网络连接管理网络请求异常处理最佳实践