HarmonyOS APP开发中的网络设置全解析——从状态监听到弱网适配,让应用永不掉线
HarmonyOS APP开发中的网络设置全解析——从状态监听到弱网适配,让应用永不掉线
📌 核心要点:connection 模块提供网络连接状态监听、网络类型判断、默认网络获取等能力,是构建健壮网络应用的基础设施
一、背景与动机
想象一下这个场景:你正在地铁上用手机看视频,列车进隧道的那一瞬间——画面卡住了、进度条转圈了、然后弹出一个冰冷的"网络连接失败"。等出了隧道,4G 信号恢复,视频又自动续播了。
这种体验,用户已经习以为常。但作为开发者,我们需要做得更好——提前感知网络变化、优雅降级、智能恢复。
HarmonyOS 的 @ohos.net.connection 模块就是为此而生。它就像一个"网络雷达",帮你实时监控网络状态的变化:WiFi 断了、切到蜂窝了、信号变弱了……你都能第一时间知道,并做出响应。
典型使用场景:
- 视频播放器:WiFi 下自动高清,蜂窝下切换标清
- 即时通讯:网络断开时消息排队,恢复后自动重发
- 文件下载:弱网自动暂停,网络恢复后续传
- 数据同步:WiFi 环境下才触发大批量同步
二、核心原理
2.1 网络连接架构
flowchart TB
A[应用层] -->|订阅| B[connection 模块]
B -->|netAvailable| C[WiFi 网络]
B -->|netAvailable| D[蜂窝网络]
B -->|netLost| E[网络断开]
B -->|netCapabilitiesChange| F[能力变更]
C -->|高带宽低延迟| G[高清模式]
D -->|带宽受限| H[省流模式]
E -->|无网络| I[离线模式]
F -->|QoS变化| J[动态适配]
classDef primary fill:#4CAF50,stroke:#388E3C,color:#fff
classDef warning fill:#FF9800,stroke:#F57C00,color:#fff
classDef error fill:#F44336,stroke:#D32F2F,color:#fff
classDef info fill:#2196F3,stroke:#1976D2,color:#fff
classDef purple fill:#9C27B0,stroke:#7B1FA2,color:#fff
class A primary
class B info
class C,D info
class E error
class F warning
class G,H,I,J purple
2.2 核心概念
| 概念 | 说明 |
|---|---|
| NetConnection | 网络连接对象,用于订阅网络状态变化 |
| NetHandle | 网络句柄,代表一个具体的网络连接 |
| NetCapabilityInfo | 网络能力信息,包含带宽、网络类型等 |
| ConnectionProperties | 连接属性,包含 DNS、网关、链路信息 |
| NetSpecifier | 网络筛选器,指定关注的网络类型 |
2.3 网络状态变化流程
sequenceDiagram
participant App as 应用
participant Conn as NetConnection
participant OS as 系统网络服务
App->>Conn: createNetConnection()
App->>Conn: on('netAvailable', callback)
App->>Conn: on('netLost', callback)
App->>Conn: on('netCapabilitiesChange', callback)
App->>Conn: register()
Note over OS: WiFi 连接
OS->>Conn: netAvailable 事件
Conn->>App: 回调通知
Note over OS: WiFi 断开,切到蜂窝
OS->>Conn: netLost 事件 (WiFi)
OS->>Conn: netAvailable 事件 (蜂窝)
Conn->>App: 连续回调
App->>Conn: unregister()
关键理解:register() 是开始监听的前提。就像你先得打开收音机,才能听到广播。同理,unregister() 是停止监听,不用时务必调用,否则会内存泄漏。
三、代码实战
3.1 网络连接状态监听:实时感知网络变化
这是最基础也最重要的能力——知道网络什么时候来了、什么时候走了、什么时候变了。
import { connection } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
/**
* 网络状态监听器
* 封装 connection 模块,提供统一的网络状态管理
*/
class NetworkMonitor {
private netConnection: connection.NetConnection | null = null;
private listeners: Map<string, Function[]> = new Map();
// 网络状态枚举
static readonly STATE_CONNECTED = 'connected';
static readonly STATE_DISCONNECTED = 'disconnected';
static readonly STATE_WIFI = 'wifi';
static readonly STATE_CELLULAR = 'cellular';
/**
* 启动网络监听
*/
startMonitor(): void {
// 创建网络连接对象
this.netConnection = connection.createNetConnection();
// 监听网络可用
this.netConnection.on('netAvailable', (data: connection.NetHandle) => {
console.info(`[Network] 网络可用: netId=${data.netId}`);
this.emit('stateChange', NetworkMonitor.STATE_CONNECTED);
this.checkNetworkType(data);
});
// 监听网络丢失
this.netConnection.on('netLost', (data: connection.NetHandle) => {
console.warn(`[Network] 网络丢失: netId=${data.netId}`);
this.emit('stateChange', NetworkMonitor.STATE_DISCONNECTED);
});
// 监听网络能力变更
this.netConnection.on('netCapabilitiesChange', (
data: connection.NetCapabilityInfo
) => {
console.info(`[Network] 能力变更: netId=${data.netHandle.netId}`);
this.analyzeCapabilities(data);
});
// 监听连接属性变更
this.netConnection.on('netConnectionPropertiesChange', (
data: connection.ConnectionProperties
) => {
console.info(`[Network] 连接属性变更`);
this.emit('propertiesChange', data);
});
// 注册监听(必须调用!)
this.netConnection.register((err: BusinessError) => {
if (err) {
console.error(`[Network] 注册失败: ${err.message}`);
} else {
console.info('[Network] 注册成功,开始监听');
}
});
}
/**
* 停止监听
*/
stopMonitor(): void {
if (this.netConnection) {
this.netConnection.unregister((err: BusinessError) => {
if (err) {
console.error(`[Network] 注销失败: ${err.message}`);
} else {
console.info('[Network] 注销成功');
}
});
this.netConnection = null;
}
}
/**
* 检查网络类型
*/
private async checkNetworkType(netHandle: connection.NetHandle): Promise<void> {
try {
const capabilities = await connection.getNetCapabilities(netHandle);
if (capabilities?.bearerTypes) {
const types = capabilities.bearerTypes;
if (types.includes(connection.NetBearType.BEARER_WIFI)) {
this.emit('typeChange', NetworkMonitor.STATE_WIFI);
console.info('[Network] 当前网络: WiFi');
} else if (types.includes(connection.NetBearType.BEARER_CELLULAR)) {
this.emit('typeChange', NetworkMonitor.STATE_CELLULAR);
console.info('[Network] 当前网络: 蜂窝');
}
}
} catch (err) {
console.error(`[Network] 获取网络类型失败: ${JSON.stringify(err)}`);
}
}
/**
* 分析网络能力
*/
private analyzeCapabilities(data: connection.NetCapabilityInfo): void {
const caps = data.netCapabilities;
const info: Record<string, Object> = {
linkUpBandwidthKbps: caps.linkUpBandwidthKbps,
linkDownBandwidthKbps: caps.linkDownBandwidthKbps,
};
// 判断是否为弱网
if (caps.linkDownBandwidthKbps && caps.linkDownBandwidthKbps < 100) {
console.warn('[Network] 弱网环境,建议降级');
this.emit('weakNetwork', true);
} else {
this.emit('weakNetwork', false);
}
this.emit('capabilitiesChange', info);
}
/**
* 注册自定义事件监听
*/
on(event: string, callback: Function): void {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event)!.push(callback);
}
/**
* 移除自定义事件监听
*/
off(event: string, callback?: Function): void {
if (!callback) {
this.listeners.delete(event);
} else {
const cbs = this.listeners.get(event);
if (cbs) {
const idx = cbs.indexOf(callback);
if (idx > -1) cbs.splice(idx, 1);
}
}
}
/**
* 触发事件
*/
private emit(event: string, ...args: Object[]): void {
const cbs = this.listeners.get(event);
if (cbs) {
cbs.forEach(cb => cb(...args));
}
}
}
export default new NetworkMonitor();
3.2 网络类型判断与切换处理:WiFi/蜂窝智能切换
视频播放器是最典型的场景——WiFi 下自动高清,蜂窝下省流模式,切换时无缝衔接。
import { connection } from '@kit.NetworkKit';
/**
* 视频画质策略
*/
enum VideoQuality {
AUTO = 'auto', // 自动
HD = 'hd', // 高清 1080P
SD = 'sd', // 标清 720P
LOW = 'low', // 流畅 480P
AUDIO_ONLY = 'audio' // 纯音频
}
/**
* 网络自适应管理器
* 根据网络状态自动调整应用行为
*/
class NetworkAdaptiveManager {
private currentQuality: VideoQuality = VideoQuality.AUTO;
private netConnection: connection.NetConnection | null = null;
/**
* 初始化网络自适应
*/
async init(): Promise<void> {
this.netConnection = connection.createNetConnection();
// 监听网络可用
this.netConnection.on('netAvailable', async (netHandle: connection.NetHandle) => {
const quality = await this.suggestQuality(netHandle);
this.applyQuality(quality);
});
// 监听网络丢失
this.netConnection.on('netLost', () => {
console.warn('[Adaptive] 网络丢失,切换到纯音频模式');
this.applyQuality(VideoQuality.AUDIO_ONLY);
});
// 监听能力变更(弱网检测)
this.netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
const downBw = data.netCapabilities.linkDownBandwidthKbps;
if (downBw && downBw < 300) {
// 下行带宽低于 300Kbps,弱网环境
console.warn(`[Adaptive] 弱网: 下行带宽 ${downBw}Kbps`);
this.applyQuality(VideoQuality.LOW);
}
});
// 注册监听
this.netConnection.register(() => {});
// 获取当前网络状态
await this.checkCurrentNetwork();
}
/**
* 检查当前网络并推荐画质
*/
private async checkCurrentNetwork(): Promise<void> {
try {
// 获取默认活跃网络
const netHandle = connection.getDefaultNetSync();
if (netHandle && netHandle.netId > 0) {
const quality = await this.suggestQuality(netHandle);
this.applyQuality(quality);
} else {
this.applyQuality(VideoQuality.AUDIO_ONLY);
}
} catch (err) {
console.error(`[Adaptive] 检查网络失败: ${JSON.stringify(err)}`);
this.applyQuality(VideoQuality.SD);
}
}
/**
* 根据网络能力推荐画质
*/
private async suggestQuality(netHandle: connection.NetHandle): Promise<VideoQuality> {
try {
const caps = await connection.getNetCapabilities(netHandle);
const bearerTypes = caps?.bearerTypes || [];
const downBw = caps?.linkDownBandwidthKbps || 0;
// WiFi 环境
if (bearerTypes.includes(connection.NetBearType.BEARER_WIFI)) {
console.info('[Adaptive] WiFi 环境,推荐高清');
return VideoQuality.HD;
}
// 蜂窝环境,根据带宽判断
if (bearerTypes.includes(connection.NetBearType.BEARER_CELLULAR)) {
if (downBw >= 2000) {
console.info(`[Adaptive] 蜂窝高速: ${downBw}Kbps,推荐标清`);
return VideoQuality.SD;
} else if (downBw >= 500) {
console.info(`[Adaptive] 蜂窝中速: ${downBw}Kbps,推荐流畅`);
return VideoQuality.LOW;
} else {
console.info(`[Adaptive] 蜂窝低速: ${downBw}Kbps,推荐纯音频`);
return VideoQuality.AUDIO_ONLY;
}
}
return VideoQuality.SD;
} catch (err) {
return VideoQuality.SD;
}
}
/**
* 应用画质变更
*/
private applyQuality(quality: VideoQuality): void {
if (this.currentQuality === quality) return;
const oldQuality = this.currentQuality;
this.currentQuality = quality;
console.info(`[Adaptive] 画质变更: ${oldQuality} → ${quality}`);
// 通知播放器切换画质
// 实际项目中通过 emitter 或状态管理通知 UI 层
}
/**
* 获取当前画质
*/
getCurrentQuality(): VideoQuality {
return this.currentQuality;
}
/**
* 销毁
*/
destroy(): void {
this.netConnection?.unregister(() => {});
this.netConnection = null;
}
}
export { NetworkAdaptiveManager, VideoQuality };
3.3 弱网适配策略:从检测到降级的完整方案
弱网不是"没网",而是"网很差"。这种场景下,应用需要主动降级,而不是让用户对着转圈发呆。
import { connection } from '@kit.NetworkKit';
import { http } from '@kit.NetworkKit';
/**
* 弱网等级
*/
enum WeakLevel {
GOOD = 'good', // 网络良好
MODERATE = 'moderate', // 网络一般
WEAK = 'weak', // 弱网
VERY_WEAK = 'very_weak', // 极弱网
OFFLINE = 'offline' // 离线
}
/**
* 弱网适配策略管理器
* 检测弱网环境,自动执行降级策略
*/
class WeakNetworkStrategy {
private currentLevel: WeakLevel = WeakLevel.GOOD;
private netConnection: connection.NetConnection | null = null;
private pingInterval: number = -1;
// 弱网阈值配置
private readonly THRESHOLDS = {
bandwidthGood: 2000, // 下行带宽 > 2Mbps 为良好
bandwidthModerate: 800, // 800Kbps ~ 2Mbps 为一般
bandwidthWeak: 200, // 200Kbps ~ 800Kbps 为弱网
// 低于 200Kbps 为极弱网
latencyGood: 200, // 延迟 < 200ms 为良好
latencyWeak: 1000, // 延迟 > 1000ms 为弱网
};
/**
* 初始化弱网检测
*/
init(): void {
this.netConnection = connection.createNetConnection();
// 监听网络状态变化
this.netConnection.on('netAvailable', (netHandle: connection.NetHandle) => {
console.info('[WeakNet] 网络恢复,重新评估');
this.evaluateNetwork(netHandle);
});
this.netConnection.on('netLost', () => {
console.warn('[WeakNet] 网络丢失');
this.applyLevel(WeakLevel.OFFLINE);
});
this.netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
this.evaluateByCapabilities(data);
});
this.netConnection.register(() => {});
// 启动主动探测(心跳检测)
this.startProbing();
}
/**
* 通过网络能力评估弱网等级
*/
private async evaluateNetwork(netHandle: connection.NetHandle): Promise<void> {
try {
const caps = await connection.getNetCapabilities(netHandle);
const downBw = caps?.linkDownBandwidthKbps || 0;
let level: WeakLevel;
if (downBw >= this.THRESHOLDS.bandwidthGood) {
level = WeakLevel.GOOD;
} else if (downBw >= this.THRESHOLDS.bandwidthModerate) {
level = WeakLevel.MODERATE;
} else if (downBw >= this.THRESHOLDS.bandwidthWeak) {
level = WeakLevel.WEAK;
} else {
level = WeakLevel.VERY_WEAK;
}
this.applyLevel(level);
} catch (err) {
console.error(`[WeakNet] 评估失败: ${JSON.stringify(err)}`);
}
}
/**
* 通过能力变更事件评估
*/
private evaluateByCapabilities(data: connection.NetCapabilityInfo): void {
const downBw = data.netCapabilities.linkDownBandwidthKbps || 0;
let level: WeakLevel;
if (downBw >= this.THRESHOLDS.bandwidthGood) {
level = WeakLevel.GOOD;
} else if (downBw >= this.THRESHOLDS.bandwidthModerate) {
level = WeakLevel.MODERATE;
} else if (downBw >= this.THRESHOLDS.bandwidthWeak) {
level = WeakLevel.WEAK;
} else {
level = WeakLevel.VERY_WEAK;
}
this.applyLevel(level);
}
/**
* 主动探测:HTTP 心跳
* 弥补被动监听的不足
*/
private startProbing(): void {
this.pingInterval = setInterval(async () => {
try {
const startTime = Date.now();
const httpRequest = http.createHttp();
const response = await httpRequest.request('https://www.huawei.cn', {
method: http.RequestMethod.HEAD,
connectTimeout: 5000,
readTimeout: 5000,
});
const latency = Date.now() - startTime;
httpRequest.destroy();
if (response.responseCode === 200) {
if (latency < this.THRESHOLDS.latencyGood) {
// 延迟正常,不改变等级
} else if (latency > this.THRESHOLDS.latencyWeak) {
// 延迟过高,降级
if (this.currentLevel === WeakLevel.GOOD) {
this.applyLevel(WeakLevel.MODERATE);
}
}
} else {
// 请求失败,可能弱网
if (this.currentLevel !== WeakLevel.OFFLINE) {
this.applyLevel(WeakLevel.VERY_WEAK);
}
}
} catch (err) {
// 请求异常,网络可能不可用
if (this.currentLevel !== WeakLevel.OFFLINE) {
this.applyLevel(WeakLevel.VERY_WEAK);
}
}
}, 30000); // 每 30 秒探测一次
}
/**
* 应用弱网等级,执行对应策略
*/
private applyLevel(level: WeakLevel): void {
if (this.currentLevel === level) return;
const oldLevel = this.currentLevel;
this.currentLevel = level;
console.info(`[WeakNet] 等级变更: ${oldLevel} → ${level}`);
// 根据等级执行策略
switch (level) {
case WeakLevel.GOOD:
this.onGoodNetwork();
break;
case WeakLevel.MODERATE:
this.onModerateNetwork();
break;
case WeakLevel.WEAK:
this.onWeakNetwork();
break;
case WeakLevel.VERY_WEAK:
this.onVeryWeakNetwork();
break;
case WeakLevel.OFFLINE:
this.onOffline();
break;
}
}
/** 网络良好策略 */
private onGoodNetwork(): void {
console.info('[Strategy] 恢复全功能模式');
// 加载高清图片、启用预加载、开启实时同步
}
/** 网络一般策略 */
private onModerateNetwork(): void {
console.info('[Strategy] 适度降级:压缩图片、减少预加载');
// 压缩图片质量、减少预加载数量、降低同步频率
}
/** 弱网策略 */
private onWeakNetwork(): void {
console.info('[Strategy] 弱网降级:仅加载缩略图、暂停后台同步');
// 仅加载缩略图、暂停后台数据同步、禁用自动播放
}
/** 极弱网策略 */
private onVeryWeakNetwork(): void {
console.info('[Strategy] 极弱网:纯文本模式、显示离线提示');
// 纯文本内容、显示"网络不佳"提示、缓存优先
}
/** 离线策略 */
private onOffline(): void {
console.info('[Strategy] 离线模式:仅展示缓存数据');
// 展示本地缓存、禁用所有网络请求、显示离线横幅
}
/**
* 获取当前弱网等级
*/
getCurrentLevel(): WeakLevel {
return this.currentLevel;
}
/**
* 销毁
*/
destroy(): void {
if (this.pingInterval !== -1) {
clearInterval(this.pingInterval);
}
this.netConnection?.unregister(() => {});
this.netConnection = null;
}
}
export { WeakNetworkStrategy, WeakLevel };
四、踩坑与注意事项
4.1 register() 和 unregister() 必须配对
坑:只调用 register() 不调用 unregister(),导致内存泄漏和重复回调。
解:在组件的 aboutToAppear 中 register,在 aboutToDisappear 中 unregister。
// ✅ 正确:配对使用
aboutToAppear() {
this.netConnection = connection.createNetConnection();
this.netConnection.on('netAvailable', this.onNetAvailable);
this.netConnection.register(() => {});
}
aboutToDisappear() {
this.netConnection?.unregister(() => {});
}
4.2 回调可能在子线程
坑:网络状态变更的回调可能在子线程执行,直接操作 UI 会崩溃。
解:通过 @State 变量或 emitter 将事件传递到主线程。
4.3 getDefaultNetSync() 可能返回无效句柄
坑:在无网络时调用 getDefaultNetSync() 返回的 netHandle.netId 为 0,后续操作会报错。
解:先判断 netId 是否大于 0。
const netHandle = connection.getDefaultNetSync();
if (netHandle.netId > 0) {
// 有活跃网络,可以继续操作
} else {
// 无网络,走离线逻辑
}
4.4 带宽值为预估值
坑:linkDownBandwidthKbps 和 linkUpBandwidthKbps 是系统预估值,不是实时测速结果。
解:仅作为参考,不要精确依赖。需要精确带宽时,应自行实现测速逻辑。
4.5 on(‘netCapabilitiesChange’) 可能频繁触发
坑:在网络波动时,netCapabilitiesChange 可能短时间内触发多次,导致频繁降级/升级。
解:加入防抖机制。
private debounceTimer: number = -1;
onNetCapabilitiesChange(data: connection.NetCapabilityInfo) {
// 防抖:500ms 内只处理最后一次
if (this.debounceTimer !== -1) {
clearTimeout(this.debounceTimer);
}
this.debounceTimer = setTimeout(() => {
this.handleCapabilitiesChange(data);
}, 500);
}
五、HarmonyOS 6 适配
5.1 API 变化一览
| 变化项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| NetBearType | WIFI / CELLULAR / ETHERNET | 新增 BEARER_BLUETOOTH |
| 网络探测 | 无内置探测 | 新增 connection.ping() 方法 |
| 网络质量评估 | 仅靠带宽预估值 | 新增 NetQuality 枚举(GOOD/MODERATE/POOR) |
| VPN 支持 | 基础支持 | 新增 VPN 连接状态监听 |
| 多网卡 | 单默认网络 | 新增 getAllNets() 获取所有活跃网络 |
5.2 迁移指南
// HarmonyOS 5:手动判断网络质量
const caps = await connection.getNetCapabilities(netHandle);
const downBw = caps.linkDownBandwidthKbps;
const isWeak = downBw < 500;
// HarmonyOS 6:直接获取网络质量
const quality = await connection.getNetQuality(netHandle);
// quality: NetQuality.GOOD / MODERATE / POOR
5.3 注意事项
- 蓝牙网络:HarmonyOS 6 新增蓝牙网络类型,如果你的应用之前只处理 WiFi/蜂窝,记得补充蓝牙场景。
- ping 方法:新增的
connection.ping()可以更准确地检测网络连通性,替代自定义的 HTTP 心跳探测。 - 多网卡:
getAllNets()返回所有活跃网络,需要根据业务选择合适的网络句柄。
六、总结
mindmap
root((网络设置))
状态监听
netAvailable
netLost
netCapabilitiesChange
netConnectionPropertiesChange
网络类型
WiFi
蜂窝
以太网
蓝牙(HarmoneyOS 6)
弱网适配
带宽评估
延迟探测
分级降级
防抖处理
降级策略
良好→全功能
一般→适度压缩
弱网→缩略图
极弱→纯文本
离线→缓存优先
注意事项
register/unregister 配对
子线程回调
带宽为预估值
防抖处理
HarmonyOS 6
蓝牙网络类型
ping 方法
NetQuality 枚举
getAllNets 多网卡
| 知识点 | 要点 |
|---|---|
| 监听三步曲 | createNetConnection → on → register,缺一不可 |
| 网络类型判断 | 通过 bearerTypes 判断 WiFi/蜂窝,决定应用行为 |
| 弱网检测 | 带宽预估值 + HTTP 心跳,双保险 |
| 降级策略 | 分级降级(良好→一般→弱网→极弱→离线),而非一刀切 |
| 防抖处理 | 网络波动时加入防抖,避免频繁切换 |
| HarmonyOS 6 | 新增蓝牙网络、ping 方法、NetQuality 枚举 |
网络就像天气——你无法控制它,但可以提前准备。掌握了状态监听和弱网适配,你的应用就能在任何网络环境下都从容不迫。
- 点赞
- 收藏
- 关注作者
评论(0)