HarmonyOS —— NetworkKit 网络连接管理实战笔记

HarmonyOS —— NetworkKit 网络连接管理实战笔记

这一节可以理解成:
“我有好几张网(Wi-Fi / 蜂窝 / 有线),系统怎么选?应用怎么跟着切?”

你这段文档主要讲的是:@kit.NetworkKit 里的 connection 模块,管理和感知网络连接。
包括:

  • 监听网络状态变化(默认网 & 指定网)
  • 多网络共存时怎么“监控 + 跟着重连”
  • 获取所有网络、默认网络、指定网络的信息
  • 判断“当前默认网络能不能上网”
  • 用默认网络做 DNS 解析

下面我按“开发视角”帮你梳一遍。

鸿蒙开发者第四期活动


一、先把几个“名词”捋清楚

文档里这几个概念很关键,考试/面试也爱问:

  • 网络生产者:提供数据网络的那一侧
    • 例:Wi-Fi、蜂窝、Ethernet
  • 网络消费者:用网络的人(或服务)
    • 例:应用、系统服务
  • 网络探测:看这条网到底能不能用,避免切到一条“摆烂网”
    • 绑定网络探测 / DNS 探测 / HTTP / HTTPS 探测
  • 网络优选:多条网络同时存在时,选哪条为“默认网”
  • 默认网络:带默认路由的网络(所有不绑网的请求通常都从这里走)

API 调用习惯:

  • 大部分都是 异步,同时提供:
    • Promise 风格
    • callback 风格(文档示例用 Promise,我们也是)

二、监听“指定网络”的状态变化

1. 权限

先在 module.json5 里声明权限:

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

权限级别是 normal,只要符合基本原则就行。

2. 创建一个“我要的网络”说明:NetSpecifier

比如:默认网络是 Wi-Fi,但我想单独弄一条蜂窝网络来用:

import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

let netSpecifier: connection.NetSpecifier = {
  netCapabilities: {
    // 我要“蜂窝网”
    bearerTypes: [connection.NetBearType.BEARER_CELLULAR],
    // 且要有上网能力(Internet)
    networkCap: [connection.NetCap.NET_CAPABILITY_INTERNET]
  },
};

// 超时时间 10s(单位 ms)
let timeout = 10 * 1000;

// 创建 NetConnection 对象
let conn = connection.createNetConnection(netSpecifier, timeout);

3. 订阅网络状态事件

// 网络可用时回调
conn.on('netAvailable', (data: connection.NetHandle) => {
  console.info('net is available, netId is ' + data.netId);
});

// 网络不可用时回调
conn.on('netUnavailable', (data: void) => {
  console.info('net is unavailable, data is ' + JSON.stringify(data));
});

4. 正式“注册”监听 & 取消

// 开始监听指定网络的状态变化(要在 on() 之后调)
conn.register((err: BusinessError) => {
  console.info(JSON.stringify(err));
});

// 不用了记得取消
conn.unregister((err: BusinessError) => {
  console.info(JSON.stringify(err));
});

心智模型:
createNetConnection = 我要什么样的网
on + register = 开始订阅它的生死状态
unregister = 不看了


三、默认网络切换时,如何“顺滑重连”?

场景:

  • Wi-Fi 很弱 → 系统把默认网切到蜂窝
  • 关了 Wi-Fi / 关了蜂窝 → 默认网切换
  • Wi-Fi 之间 / 蜂窝之间跨网切换

对你来说,重点是:

“默认网变了,原来的 Socket 可能废了,要重建连接。”

1. 监听默认网络变化

import { connection } from '@kit.NetworkKit';

const netConnection = connection.createNetConnection();

// 这里不传 NetSpecifier,就是“盯默认网络”
netConnection.on('netAvailable', (data: connection.NetHandle) => {
  console.info('default network changed: ' + JSON.stringify(data));
});

2. 如果你用 socket 建连接,怎么做?

import { connection, socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 全局持有一个 socket
let sock: socket.TCPSocket = socket.constructTCPSocketInstance();

function useSocket() {
  const address: socket.NetAddress = {
    address: '192.168.xx.xxx',
    port: 8080,
  };
  const tcpConnectOptions: socket.TCPConnectOptions = {
    address,
    timeout: 6000,
  };

  sock.connect(tcpConnectOptions, (err: BusinessError) => {
    if (err) {
      console.error('connect fail: ' + JSON.stringify(err));
      return;
    }
    console.info('connect success');

    const sendOptions: socket.TCPSendOptions = {
      data: 'Hello, server!',
    };
    socketSend(sendOptions);
  });
}

function socketSend(sendOptions: socket.TCPSendOptions) {
  sock
    .send(sendOptions)
    .then(() => console.info('send success'))
    .catch((err: BusinessError) => console.error('send fail: ' + JSON.stringify(err)));
}

把“重连逻辑”挂在默认网络变化上:

function socketTest() {
  const netConnection = connection.createNetConnection();

  netConnection.on('netAvailable', (netHandle: connection.NetHandle) => {
    console.info('default network changed: ' + JSON.stringify(netHandle));

    // 原连接作废,先关掉
    sock.close();
    // 新建一个 socket 实例
    sock = socket.constructTCPSocketInstance();
    // 再次建立连接并发送数据
    useSocket();
  });

  // 注册监听
  netConnection.register((error: BusinessError) => {
    if (error) {
      console.error('register fail: ' + JSON.stringify(error));
    } else {
      console.info('register success');
    }
  });
}

一句话:“默认网一变,socket 全部重建一遍。”

如果你用的是 Socket Library(另一套封装),思路也是一样:监听 → 关闭旧连接 → 重连


四、获取“所有注册网络”

目标:看看当前设备有哪些网络“处于连接状态”。

import { connection } from '@kit.NetworkKit';

// 需要 ohos.permission.GET_NETWORK_INFO
connection.getAllNets().then((data: connection.NetHandle[]) => {
  console.info('Succeeded to get data: ' + JSON.stringify(data));
  if (data) {
    // 比如丢到全局上下文里存起来
    GlobalContext.getContext().netList = data;
  }
});

这里拿到的是一组 NetHandle,后面可以配合 getNetCapabilities / getConnectionProperties 查看每条网络的详细信息。


五、查询默认网络 / 所有网络的详细信息

1. 查默认网络信息

import { connection } from '@kit.NetworkKit';

function getDefaultNetsInfo() {
  let netHandleInfo: connection.NetHandle | null = null;

  connection.getDefaultNet().then((data: connection.NetHandle) => {
    if (data.netId === 0) {
      console.info("don't have defaultNet");
      return;
    }

    if (data) {
      console.info('getDefaultNet get data: ' + JSON.stringify(data));
      netHandleInfo = data;

      // 能力信息:类型 + 能力
      connection.getNetCapabilities(netHandleInfo).then((caps: connection.NetCapabilities) => {
        console.info('getNetCapabilities get data: ' + JSON.stringify(caps));

        // 网络类型:bearerTypes
        const bearerSet = new Set(caps.bearerTypes);
        for (const item of Array.from(bearerSet.values())) {
          if (item === connection.NetBearType.BEARER_CELLULAR) {
            console.info('BEARER_CELLULAR');
          } else if (item === connection.NetBearType.BEARER_WIFI) {
            console.info('BEARER_WIFI');
          } else if (item === connection.NetBearType.BEARER_ETHERNET) {
            console.info('BEARER_ETHERNET');
          }
        }

        // 网络能力:networkCap
        const capSet = new Set(caps.networkCap);
        for (const item of Array.from(capSet.values())) {
          if (item === connection.NetCap.NET_CAPABILITY_MMS) {
            console.info('NET_CAPABILITY_MMS');
          } else if (item === connection.NetCap.NET_CAPABILITY_NOT_METERED) {
            console.info('NET_CAPABILITY_NOT_METERED');
          } else if (item === connection.NetCap.NET_CAPABILITY_INTERNET) {
            console.info('NET_CAPABILITY_INTERNET');
          } else if (item === connection.NetCap.NET_CAPABILITY_NOT_VPN) {
            console.info('NET_CAPABILITY_NOT_VPN');
          } else if (item === connection.NetCap.NET_CAPABILITY_VALIDATED) {
            console.info('NET_CAPABILITY_VALIDATED');
          }
        }
      });
    }
  });

  // 连接属性(IP、路由等)
  connection.getConnectionProperties(netHandleInfo).then((props: connection.ConnectionProperties) => {
    console.info('getConnectionProperties get data: ' + JSON.stringify(props));
  });
}

2. 查所有网络的详细信息

import { connection } from '@kit.NetworkKit';

function getAllNetsInfo() {
  connection.getAllNets().then((handles: connection.NetHandle[]) => {
    console.info('getAllNets get data: ' + JSON.stringify(handles));
    if (!handles) return;

    const handleSet = new Set(handles);
    for (const h of Array.from(handleSet.values())) {
      // 每条网络的能力
      connection.getNetCapabilities(h).then((caps) => {
        console.info('getNetCapabilities get data: ' + JSON.stringify(caps));
      });

      // 每条网络的连接属性
      connection.getConnectionProperties(h).then((props) => {
        console.info('getConnectionProperties get data: ' + JSON.stringify(props));
      });
    }
  });
}

六、判断“当前默认网络能不能上网”

核心能力:networkCap 是否包含 NET_CAPABILITY_VALIDATED

还有一个 NET_CAPABILITY_CHECKING_CONNECTIVITY 表示“正在校验中”。

import { connection } from '@kit.NetworkKit';

// 同步获取默认网
const netHandle = connection.getDefaultNetSync();

if (!netHandle || netHandle.netId === 0) {
  console.error('getDefaultNetSync fail');
} else {
  console.info('default network: ' + JSON.stringify(netHandle));

  const netCapabilities = connection.getNetCapabilitiesSync(netHandle);
  const cap = netCapabilities.networkCap;
  console.info('network capabilities: ' + JSON.stringify(netCapabilities));

  if (cap?.includes(connection.NetCap.NET_CAPABILITY_CHECKING_CONNECTIVITY)) {
    // 正在做连通性判断
    console.info('default network is checking, please try again later');
  } else {
    if (cap?.includes(connection.NetCap.NET_CAPABILITY_VALIDATED)) {
      // 连通性验证通过:可访问互联网
      console.info('default network is validated');
    } else {
      // 验证失败:这网连不上外网
      console.info('default network is not validated');
    }
  }
}

这个逻辑在“网络质量判断 + 动态降级策略”里很好用,
例如:验证失败就只展示本地缓存 / 提示用户换网络。


七、用默认网络做 DNS 解析

最后一个小能力:用当前默认网解析域名 → 拿到 IP 列表

import { connection } from '@kit.NetworkKit';

// "xxxx" 替换成真正要解析的域名
connection.getAddressesByName('xxxx').then((data: connection.NetAddress[]) => {
  console.info('Succeeded to get data: ' + JSON.stringify(data));
});

这里同样受网络类型 & 默认网络影响,比如默认走的是 VPN,就会用 VPN 那边的 DNS。


八、你可以怎么用在实际项目里?

给你几个可以直接套用的思路:

  1. IM / 长连接类 App
    • 监听默认网络变化 → 自动重建 TCP / WebSocket 连接
    • 判断 NET_CAPABILITY_VALIDATED → 决定是否展示“当前网络不可用”提示
  2. 多媒体 / 下载器
    • 枚举所有网络 → 做自己的“网络优选策略”(例如更倾向 Wi-Fi + notMetered)
    • getAddressesByName显式 DNS 解析 + 打点
  3. 诊断工具 / 开发者选项
    • 展示当前所有网络的:
      • 类型(Wi-Fi / 蜂窝 / 以太网)
      • 能力(INTERNET / VALIDATED / NOT_METERED 等)
    • 做一个“网络体检”页面
posted @ 2025-12-13 20:23  遇到困难睡大觉哈哈  阅读(5)  评论(0)    收藏  举报