HarmonyOS Network Kit 网络状态治理:默认网络监听、能力判断与断网恢复

移动应用里“有网”不等于“业务可用”。默认网络切换、能力变化、弱网恢复和离线重试如果散落在页面里,用户会看到请求转圈、重复提交或错误提示乱跳。本文把 Network Kit 的默认网络监听、能力判断和请求恢复队列放在同一套状态机里处理。

请添加图片描述

本文先把网络状态误判说清

这一节从用户真实体验切入:状态栏显示已联网,并不代表业务接口能正常访问;Wi-Fi 可用,也不代表适合上传大文件。网络状态治理要把平台网络、业务策略、离线队列和页面反馈放在一起,否则用户看到的就是反复失败和重复提交。

  • 启动时先读取默认网络,避免只等回调造成初始状态错误。
  • 把 INTERNET、VALIDATED、NOT_METERED 转成业务可读状态。
  • 断网任务进入可去重队列,网络恢复后串行执行。
  • 页面只展示状态和进度,不直接持有 NetConnection。

这几件事连在一起看,读者就能从配置、API 调用、状态维护和排查方法四个角度复用本文方案,而不是只复制某一段示例代码。

Network Kit 资料与声明入口

项目内容
官方能力Network Kit 提供默认网络、网络能力、连接属性和网络回调。
本地声明D:/harmonyos/SDK/23/ets/api/@ohos.net.connection.d.ts
关键事件netAvailable、netCapabilitiesChange、netLost。
权限查询网络状态通常需要 ohos.permission.GET_NETWORK_INFO,发起网络请求还需要 INTERNET。

网络监听的权限与运行边界

项目内容
SDKHarmonyOS SDK 23,已核对 createNetConnection、getDefaultNet、getNetCapabilities。
适用场景登录、同步、地图、媒体加载、离线队列恢复。
边界网络可用不代表服务可达,服务端健康仍需应用层探测。
线程网络状态中心应是应用级服务,不建议每个页面重复注册。

请添加图片描述

请添加图片描述

权限先声明,页面再谈状态

没有网络信息权限时,监听代码写得再完整也拿不到预期结果。把权限和业务网络请求权限分开看,排查会更快。

{
  "module": {
    "requestPermissions": [
      { "name": "ohos.permission.GET_NETWORK_INFO" },
      { "name": "ohos.permission.INTERNET" }
    ]
  }
}

GET_NETWORK_INFO 负责网络状态查询,INTERNET 负责实际请求。两者缺一项,表现出来的问题可能完全不同。

用统一模型承接默认网络

页面不应该直接判断 NetHandle。先把默认网络转换成业务可读的 NetworkSnapshot,后续 UI、仓储、重试队列都读这一份状态。

export interface NetworkSnapshot {
  online: boolean;
  validated: boolean;
  metered: boolean;
  handleId: number;
  reason: string;
}

export const OfflineSnapshot: NetworkSnapshot = {
  online: false,
  validated: false,
  metered: true,
  handleId: -1,
  reason: 'no default network'
};

模型层把平台对象和业务状态隔离开。页面只关心 online、validated 和 metered,不依赖 NetHandle 的内部结构。

初始化时先读一次默认网络

只注册回调会漏掉应用启动时已经存在的网络。状态中心启动时先读取当前默认网络,再注册后续变化。

import connection from '@ohos.net.connection';

export async function readDefaultNetwork(): Promise<NetworkSnapshot> {
  const handle = await connection.getDefaultNet();
  if (!handle || handle.netId === 0) {
    return OfflineSnapshot;
  }
  const cap = await connection.getNetCapabilities(handle);
  const caps = cap.networkCap ?? [];
  return {
    online: caps.includes(connection.NetCap.NET_CAPABILITY_INTERNET),
    validated: caps.includes(connection.NetCap.NET_CAPABILITY_VALIDATED),
    metered: !caps.includes(connection.NetCap.NET_CAPABILITY_NOT_METERED),
    handleId: handle.netId,
    reason: 'default network ready'
  };
}

这段代码完成冷启动快照。它不把“有默认网络”等同于“业务可请求”,而是继续读取能力列表。

监听回调要更新同一份状态

netAvailable、netCapabilitiesChange 和 netLost 分别对应接入、能力变化和丢失。三者都应该写到状态中心,而不是直接触发页面请求。

export class NetworkStateCenter {
  private conn = connection.createNetConnection();
  private snapshot: NetworkSnapshot = OfflineSnapshot;

  async start(): Promise<void> {
    this.snapshot = await readDefaultNetwork();
    this.conn.on('netAvailable', async () => this.snapshot = await readDefaultNetwork());
    this.conn.on('netCapabilitiesChange', async () => this.snapshot = await readDefaultNetwork());
    this.conn.on('netLost', () => this.snapshot = { ...OfflineSnapshot, reason: 'network lost' });
    await this.conn.register();
  }

  async stop(): Promise<void> {
    await this.conn.unregister();
  }

  current(): NetworkSnapshot {
    return this.snapshot;
  }
}

状态中心拥有监听器生命周期。页面读取 current,不注册自己的 NetConnection,减少重复回调和内存泄漏。

请求前判断能力,不在页面里猜

地图、图片和同步任务对网络要求不同。请求前通过策略判断是否允许执行,比在每个页面里写 if 更稳定。

export function canRunSync(snapshot: NetworkSnapshot): boolean {
  return snapshot.online && snapshot.validated;
}

export function canLoadLargeMedia(snapshot: NetworkSnapshot): boolean {
  return snapshot.online && snapshot.validated && !snapshot.metered;
}

策略函数只接受业务状态。它把联网能力和业务成本区分开,大文件加载可以避开按量网络。

断网队列要可去重

断网时把所有请求直接重试,会制造重复提交。队列项必须有稳定 id,并声明是否可合并。

export interface RetryJob {
  id: string;
  createdAt: number;
  action: 'sync-note' | 'upload-track' | 'refresh-profile';
  payload: Record<string, string>;
}

export class RetryBox {
  private jobs = new Map<string, RetryJob>();

  put(job: RetryJob): void {
    this.jobs.set(job.id, job);
  }

  drain(): RetryJob[] {
    const values = Array.from(this.jobs.values());
    this.jobs.clear();
    return values;
  }
}

Map 用 job.id 去重。网络恢复时 drain 一次,执行失败再重新入队,避免无限重复。

恢复执行要串行控制

网络抖动时回调可能连续触发。恢复队列要有 running 标记,避免同一批任务并发执行。

export class NetworkRecoveryRunner {
  private running = false;
  constructor(private center: NetworkStateCenter, private box: RetryBox) {}

  async tryRecover(): Promise<void> {
    if (this.running || !canRunSync(this.center.current())) {
      return;
    }
    this.running = true;
    try {
      for (const job of this.box.drain()) {
        await SyncService.run(job);
      }
    } finally {
      this.running = false;
    }
  }
}

恢复器的边界是断网任务执行。它不负责监听网络,只在状态满足时消费队列。

UI 展示要区分离线和受限网络

用户看到“失败”很难判断是否需要重试。把 offline、metered、validated 分开展示,能减少误操作。

export function networkHint(snapshot: NetworkSnapshot): string {
  if (!snapshot.online) {
    return '当前离线,操作会暂存';
  }
  if (!snapshot.validated) {
    return '网络已连接,服务暂不可达';
  }
  if (snapshot.metered) {
    return '当前可能为按量网络,大文件稍后同步';
  }
  return '网络正常';
}

提示文案来自状态模型,不来自异常字符串。这样各页面能保持一致的用户反馈。

Network Kit 排查表:从离线假象倒查

现象优先查看处理方式
应用启动后状态一直离线是否只注册回调,没有读取默认网络启动时先执行 readDefaultNetwork。
有 Wi-Fi 但请求失败是否只判断 INTERNET,没有判断 VALIDATED状态模型加入 validated 字段。
恢复后重复提交断网队列是否用稳定 id 去重用 Map 或本地表以业务 id 去重。
离开页面后仍回调NetConnection 生命周期是否绑定到页面改为应用级状态中心或明确 stop。

网络恢复前的核对清单

这份清单建议在提交代码、写入团队文档或交给测试同学前逐项过一遍。它不是形式化备注,而是把本文的配置边界、运行时行为、异常兜底和可观测信息压成可以执行的确认项。

  • module.json5 已声明网络信息和请求权限。
  • 状态中心启动时先读取当前默认网络。
  • 业务请求前区分 online、validated、metered。
  • 断网队列有去重 id 和串行恢复保护。
  • 页面不直接持有 NetConnection。

如果其中任意一项还没有办法给出明确证据,优先回到对应实现小节补日志、补校验或补生命周期处理,再进入下一轮联调。

网络状态治理小结

网络治理的关键是把平台网络变化转换成业务状态,再由策略和队列消费这份状态。这样断网、弱网和恢复都能被解释,也能被验证。

Network Kit 参考资料

下面列出的资料用于核对 API 名称、能力范围和版本边界。实际落地时还需要结合项目使用的 SDK 版本、设备 API 级别以及团队已有封装做一次复核。

  • Network Kit API 声明参考:https://developer.huawei.com/consumer/en/doc/harmonyos-references-V14/_net_connection-V14
  • HarmonyOS SDK 23 本地 API 声明:@ohos.net.connection.d.ts
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐