欢迎光临
我们一直在努力

HarmonyOS 网络请求稳定性实战:超时、重试、弱网兜底与状态恢复

HarmonyOS 网络请求稳定性实战:超时、重试、弱网兜底与状态恢复

网络请求失败不一定是接口挂了。移动场景里更常见的是弱网抖动、切后台、DNS 延迟、服务端偶发 5xx、用户重复点击、页面销毁后回调仍然更新状态。稳定的网络层不能只封装一个 request,还要处理超时、错误分类、重试、缓存兜底和页面状态恢复。

请添加图片描述

一、先定义请求上下文

请求上下文要包含业务场景、超时、重试次数和缓存策略。这样同一个网络层可以服务不同页面。

场景策略
首页列表 可重试,可读缓存
提交表单 不自动重试,避免重复提交
搜索建议 短超时,失败静默
详情页 可重试,失败展示重试按钮

请添加图片描述

二、版本与边界说明

本文以 HarmonyOS / ArkTS 网络请求封装为背景,示例不绑定具体服务端协议。真实项目可以接入 @ohos.net.http、团队 HTTP SDK 或网关层,但请求上下文、错误映射和恢复策略应保持独立。

建议记录以下环境:

项目说明
API 版本 当前 HarmonyOS SDK
网络库 系统 HTTP 或团队封装
服务端协议 REST、GraphQL 或自定义协议
缓存层 内存、Preferences、文件或数据库

三、请求模型:不要只传 URL

只传 URL 会让网络层不知道该不该重试、该不该缓存、失败后怎么提示。

export interface RequestContext {
id: string;
url: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
timeoutMs: number;
retryTimes: number;
cacheKey?: string;
scene: 'list' | 'detail' | 'submit' | 'search';
}

代码解释:

  • scene 决定错误提示和恢复策略。
  • retryTimes 明确最大重试次数。
  • cacheKey 存在时才允许缓存兜底。
  • 请求上下文让网络层不再盲目处理。
  • 请添加图片描述

    四、错误分类:不是所有失败都能重试

    4xx 多半是客户端参数或权限问题,盲目重试没有意义;网络断开和 5xx 才更适合重试。

    export type RequestErrorType = 'network' | 'timeout' | 'server' | 'client' | 'business';

    export function canRetry(type: RequestErrorType): boolean {
    return type === 'network' || type === 'timeout' || type === 'server';
    }

    代码解释:

  • 错误类型先归类,再决定策略。
  • 客户端错误不自动重试,避免浪费请求。
  • 业务错误交给页面显示具体原因。
  • 错误分类是稳定请求的基础。
  • 五、指数退避:重试要克制

    重试不能立刻连续打三次,否则弱网下会加重拥塞。指数退避能给网络恢复时间。

    export function getRetryDelay(attempt: number): number {
    const base = 500;
    const max = 8000;
    return Math.min(base * Math.pow(2, attempt), max);
    }

    代码解释:

  • 第一次重试等待较短,后续逐步增加。
  • max 防止等待时间无限增长。
  • 表单提交类请求不建议自动重试。
  • 重试策略要和错误分类配合使用。
  • 六、缓存兜底:失败时保留可用内容

    列表和详情页可以在请求失败时读取上一次成功缓存,让页面不至于空白。

    export interface CacheRecord<T> {
    value: T;
    savedAt: number;
    }

    export function isCacheFresh<T>(record: CacheRecord<T>, ttlMs: number): boolean {
    return Date.now() record.savedAt <= ttlMs;
    }

    代码解释:

  • 缓存要有时间戳,避免长期展示过期数据。
  • ttlMs 按业务场景配置。
  • 缓存兜底适合展示型数据,不适合支付或提交结果。
  • 页面要提示“当前为缓存数据”,避免误导用户。
  • 七、页面状态恢复:请求失败也要有确定状态

    页面状态不能只有成功和加载。弱网下要能表达缓存、失败、重试中和空数据。

    export type PageDataState = 'idle' | 'loading' | 'success' | 'cacheFallback' | 'failed';

    export interface NetworkPageState<T> {
    state: PageDataState;
    data?: T;
    message?: string;
    }

    代码解释:

  • cacheFallback 明确说明当前数据来自缓存。
  • failed 用于展示重试入口。
  • message 给用户可理解的提示。
  • 页面状态清楚后,弱网体验会稳定很多。
  • 八、防止过期回调:页面销毁后不更新状态

    网络请求返回时,页面可能已经退出。需要用请求 id 或生命周期标记避免旧回调覆盖新状态。

    export class RequestToken {
    private active = true;

    cancel(): void {
    this.active = false;
    }

    canUpdate(): boolean {
    return this.active;
    }
    }

    代码解释:

  • 页面退出时调用 cancel。
  • 请求回调前检查 canUpdate。
  • 这样可以避免页面销毁后仍更新状态。
  • 搜索、下拉刷新、快速切页都需要类似保护。
  • 九、验证流程

    1. 正常网络打开列表,确认请求成功并写入缓存。
    2. 切到弱网,确认超时和重试策略生效。
    3. 断网后进入页面,确认可读取缓存兜底。
    4. 表单提交失败时确认不会自动重复提交。
    5. 快速进入再退出页面,确认旧回调不更新 UI。
    6. 查看日志,确认错误类型和重试次数可追踪。

    十、常见问题排查

    现象可能原因修复建议
    弱网一直转圈 没有超时 设置 timeoutMs
    重复提交订单 POST 自动重试 提交类禁用自动重试
    失败页面空白 没有缓存兜底 增加 cacheFallback
    返回后崩溃 旧回调更新页面 加入 RequestToken
    错误提示混乱 未分类错误 使用 RequestErrorType

    十一、验收清单

    检查项是否完成
    请求有超时配置
    错误类型能区分
    可重试和不可重试场景分开
    缓存兜底有过期时间
    页面销毁后不会更新状态

    弱网验收要覆盖断网、慢网、请求中切后台、请求中返回上一页。只测正常 Wi-Fi,网络层问题基本看不出来。

    还要看“用户重复触发”。比如用户连续下拉刷新、连续点击提交按钮、搜索框快速输入,这些场景会产生多个并发请求。如果没有请求 token、节流或取消机制,旧请求可能覆盖新结果,页面就会出现数据倒退。

    十二、工程落地建议

    可以先从首页列表接入稳定请求链路,因为它既需要加载速度,也需要弱网兜底。首页跑通后,再把详情页、搜索页和提交页按各自策略接入,不要所有请求套同一套重试规则。

    建议把网络请求分成三类治理:查询类、提交类、实时类。查询类可以重试和读缓存,提交类必须防重复,实时类要关注连接状态和恢复策略。分类之后,网络层的默认策略才不会误伤业务。

    上线后可以记录脱敏指标:请求耗时、错误类型、重试次数、缓存命中、最终状态。这些指标能帮助团队判断问题到底来自服务端、网络环境还是客户端状态管理。不要只记录“请求失败”,那样无法定位。

    如果服务端支持幂等键,提交类请求最好带上业务幂等标识。这样即使用户重复点击或网络层发生重试,服务端也能识别同一笔业务操作,避免重复创建订单、重复收藏或重复提交表单。

    弱网兜底也要避免过度乐观。缓存数据应有时间标识,页面可以提示“当前展示上次成功加载的数据”。让用户知道数据来源,比悄悄展示过期内容更可靠。

    并发请求也要治理。搜索框输入、筛选条件切换、下拉刷新都可能在短时间内发起多次请求。建议给每个请求带上序号,页面只接受最后一次请求的结果。否则旧请求慢一步返回,就可能覆盖新数据。

    export class LatestRequestGuard {
    private latestId = 0;

    next(): number {
    this.latestId += 1;
    return this.latestId;
    }

    isLatest(id: number): boolean {
    return id === this.latestId;
    }
    }

    代码解释:

  • 每次发起请求前调用 next 生成新的请求序号。
  • 回调返回时用 isLatest 判断是否仍是最新请求。
  • 旧请求结果直接丢弃,避免覆盖新页面状态。
  • 搜索、筛选、分页刷新都适合接入这个保护。
  • 网络层还要和页面生命周期配合。页面退出时不一定能取消底层请求,但至少不能让回调继续更新 UI。RequestToken 和 LatestRequestGuard 可以组合使用:前者处理页面是否还活着,后者处理结果是否过期。

    对于缓存兜底,要区分“可展示缓存”和“可提交缓存”。列表、详情、资讯内容可以展示缓存;支付结果、库存状态、权限状态不能随便用旧数据兜底。不同数据类型要有明确规则。

    发布前还要检查错误文案。网络错误、服务端错误、登录失效、业务失败应该给不同提示。用户看到“网络异常”却实际是登录过期,会误判问题,也会增加客服成本。

    错误文案最好和错误类型一一对应,避免所有失败都落到同一个 Toast。

    这会直接影响用户是否愿意再次尝试。

    十三、总结

    网络稳定性的核心不是多重试几次,而是让请求行为可预测:超时有边界、错误能分类、重试有节制、缓存能兜底、页面状态可恢复。做到这些,弱网体验才会从“碰运气”变成可治理。

    赞(0)
    未经允许不得转载:171主机测评 » HarmonyOS 网络请求稳定性实战:超时、重试、弱网兜底与状态恢复
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址