HarmonyOS 鸿蒙Next开发者技术支持 - 基于 Network Kit 的网络状态监听与视频播放控制技术总结
HarmonyOS 鸿蒙Next开发者技术支持 - 基于 Network Kit 的网络状态监听与视频播放控制技术总结
一、关键技术总结
1. 问题说明
(一)网络状态无感知,视频策略脱节
WiFi 与蜂窝网络切换、断网或弱网时,应用未实时响应,视频仍按原策略播放。如开启 “仅 WiFi 自动播放”,WiFi 断开后未暂停,耗蜂窝流量;弱网无提示,卡顿严重却无反馈,影响体验。
(二)自动播放设置不持久,状态不同步
设置的 WiFi / 蜂窝自动播放开关,未持久化存储,应用重启后重置;页面与设置状态未实时同步,修改开关后,视频播放策略未即时更新,出现 “设置关却播放” 的矛盾。
(三)资源管理混乱,监听未闭环
页面销毁时未关闭网络监听,重复创建监听实例;网络事件传递延迟,状态变化后未及时触发视频控制,如网络恢复后视频未自动重启。
2. 原因分析
(一)网络监听能力缺失
未集成 Network Kit 的netAvailable
、netLost
等事件,无法实时获取网络状态;未区分 WiFi / 蜂窝类型单独处理,导致策略无法差异化适配。
(二)状态持久化机制断层
用普通变量存储设置,未通过PersistentStorage
持久化;未借助AppStorage
同步页面与设置状态,修改后无法即时同步至播放逻辑。
(三)资源生命周期失控
在build
生命周期创建监听,页面重绘导致多实例冲突;页面销毁未调用unregister
关闭监听,实例残留占用资源,新监听无法正常初始化。
3. 解决思路
(一)构建全场景网络监听体系
基于 Network Kit 订阅netAvailable
、netLost
、WeakNet
等事件,区分 WiFi / 蜂窝类型,实时传递状态至页面,为视频控制提供依据。
(二)设置持久化与状态同步
用PersistentStorage
持久化自动播放设置,确保重启不丢失;通过AppStorage
实现页面与设置的状态联动,修改后即时更新播放策略。
(三)标准化资源生命周期管理
在aboutToAppear
初始化监听,aboutToDisappear
调用unregister
释放;用单例模式管理监听实例,避免重复创建,确保事件传递高效。
4. 解决方案
(一)网络监听工具类
单例封装 Network Kit 监听与事件传递,统一处理网络状态:
import { connection } from '[@kit](/user/kit).NetworkKit';
import { BusinessError, emitter } from '[@kit](/user/kit).BasicServicesKit';
import { radio } from '[@kit](/user/kit).TelephonyKit';
import { wifiManager } from '[@kit](/user/kit).ConnectivityKit';
import { logger } from './Logger';
import { netQuality } from '[@kit](/user/kit).NetworkBoostKit';
import { NetworkEventData } from './EmitterData';
import { HashMap, JSON } from '[@kit](/user/kit).ArkTS';
type NetworkData = boolean | connection.NetBlockStatusInfo | connection.NetBearType
| connection.NetConnectionPropertyInfo | connection.NetCapabilityInfo;
// 网络监听emitter事件
export enum NetworkEventName {
// 注册网络监听订阅事件
NetObserverRegister,
// 网络可用
NetAvailable,
// 网络阻塞
NetBlock,
// 网络丢失/断开
NetLost,
// 当网络能力变化时,如网络从无网络到有网络、从4G切换到5G
NetCapabilitiesChange,
// 网络不可用
NetUnavailable,
// WIFI状态改变
WifiStateChange,
// WIFI连接状态改变
WifiConnectionChange,
// 弱网
WeakNet,
// 订阅网络连接信息变化事件,当网络连接信息变化时,如从无网络到有网络、从Wi-Fi切换到蜂窝
NetConnectionPropertiesChange
}
export class CellularLinkNetUtils {
public static instance: NetUtils;
private connectionMap: HashMap<connection.NetBearType, connection.NetConnection> = new HashMap();
// 网络状态监听eventId
private networkEventId: number = 10001;
// 网络监听相关结果数据
private emitterEvent: NetworkEventData;
constructor() {
this.emitterEvent = new NetworkEventData(this.networkEventId);
}
static getInstance(): NetUtils {
if (!NetUtils.instance) {
NetUtils.instance = new NetUtils();
}
return NetUtils.instance;
}
public getEmitterEvent(): NetworkEventData {
return this.emitterEvent;
}
private setEventPriority(priority: emitter.EventPriority): void {
this.emitterEvent.priority = priority;
}
private postEvent(eventName: NetworkEventName, status: NetworkData, netType?: connection.NetBearType,
priority?: emitter.EventPriority) {
this.emitterEvent.priority = priority;
emitter.emit(this.emitterEvent, {
data: new NetEventData(eventName, status, netType)
})
}
//开启网络监听
public startNetObserve(...netType: connection.NetBearType[]) {
netType.forEach((type: connection.NetBearType) => {
this.networkObserve(type);
if (type === connection.NetBearType.BEARER_WIFI) {
this.wifiStateObserve();
}
})
}
// 停止网络监听
public stopNetObserve(netType: connection.NetBearType) {
this.connectionMap.get(netType).unregister(() => {
logger.info("Success unregister:" + netType.toString());
})
}
// 停止所有网络监听
public stopAllNetObserve() {
emitter.off(this.getEmitterEvent().eventId);
this.connectionMap.forEach((netConnection: connection.NetConnection, netType: connection.NetBearType) => {
netConnection.unregister(() => {
logger.info("Success unregister:" + netType.toString());
});
})
}
getNetworkConnectionType(): Array<connection.NetBearType> {
try {
// 获取默认激活的数据网络
let netHandle = connection.getDefaultNetSync();
if (!netHandle || netHandle.netId === 0) {
return [];
}
// 获取网络的类型、拥有的能力等信息
let netCapability = connection.getNetCapabilitiesSync(netHandle);
return netCapability.bearerTypes;
} catch (e) {
let err = e as BusinessError;
logger.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
return [];
}
}
judgeHasNet(): boolean {
try {
let netHandle = connection.getDefaultNetSync();
if (!netHandle || netHandle.netId === 0) {
return false;
}
let netCapability = connection.getNetCapabilitiesSync(netHandle);
let cap = netCapability.networkCap || [];
if (cap.includes(connection.NetCap.NET_CAPABILITY_VALIDATED)) {
//connection.NetCap.NET_CAPABILITY_VALIDATED,该值代表网络是通的,能够发起HTTP和HTTPS的请求。
// 网络信息变化,网络可用
return true;
} else {
// 网络信息变化,网络不可用
return false;
}
} catch (e) {
let err = e as BusinessError;
logger.error("JudgeHasNet" + JSON.stringify(err));
}
return false;
}
// 获取网络状态,查询手机卡注册网络的运营商名称、是否处于漫游状态、设备的网络注册状态等信息
getNetworkStatus() {
radio.getNetworkState((err: BusinessError, data: radio.NetworkState) => {
if (err) {
logger.error(`getNetworkState failed, callback: err->${JSON.stringify(err)}`);
}
// regState字段表示设备的网络注册状态
// (REG_STATE_POWER_OFF,值为3)蜂窝无线电已关闭,modem下电,无法和网侧进行通信
logger.info("Success getNetworkStatus:" + JSON.stringify(data));
});
}
async getSignalType(): Promise<radio.SignalInformation[]> {
let slotId: number = await radio.getPrimarySlotId();
let data: Array<radio.SignalInformation> = radio.getSignalInformationSync(slotId);
// signalType代表网络类型NetworkType
let signalType = data[0].signalType;
logger.info("getSignalType:" + JSON.stringify(data));
return data;
}
getWifiStatus(): boolean {
try {
let isWifiActive: boolean = wifiManager.isWifiActive();
return isWifiActive;
} catch (error) {
logger.error("failed:" + JSON.stringify(error));
}
return false;
}
getWifiIsConnected(): boolean {
try {
let ret = wifiManager.isConnected();
logger.info("isConnected:" + ret);
return ret;
} catch (error) {
logger.error("failed:" + JSON.stringify(error));
}
return false;
}
async getSignalLevel(): Promise<number> {
try {
let wifiLinkedInfo: wifiManager.WifiLinkedInfo = await wifiManager.getLinkedInfo();
let rssi = wifiLinkedInfo.rssi;
let band = wifiLinkedInfo.band;
let level = wifiManager.getSignalLevel(rssi, band);
logger.info("level:" + JSON.stringify(level));
return level;
} catch (error) {
logger.error("failed:" + JSON.stringify(error));
}
return -1;
}
networkObserve(netType: connection.NetBearType) {
// TODO:根据网络类型,设置不同的网络监听,用于WI-FI和蜂窝网络切换时判断各自网络状态的变化。
let netConnection: connection.NetConnection = connection.createNetConnection({
netCapabilities: {
bearerTypes: [netType]
}
})
// 注册网络监听,注册成功后才能监听到对应类型的网络状态变化
netConnection.register((error: BusinessError) => {
let result = true;
if (error) {
logger.info("NetUtils", "NetType :" + netType + ", network register failed: " + JSON.stringify(error));
result = false;
}
logger.info("NetUtils", "NetType :" + netType + ", network register succeed");
this.postEvent(NetworkEventName.NetObserverRegister, result, netType);
});
// 网络能力改变监听,当网络能力变化时,如网络从无网络到有网络、从4G切换到5G时,会触发该事件。
netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
logger.info("NetUtils", "NetType :" + netType + ", network netCapabilitiesChange: " + JSON.stringify(data));
this.postEvent(NetworkEventName.NetCapabilitiesChange, data, netType);
})
// 网络可用监听,当网络可用时触发该事件。
netConnection.on("netAvailable", (data: connection.NetHandle) => {
logger.info("NetUtils",
"NetType :" + netType + ", network succeeded to get netAvailable: " + JSON.stringify(data));
// 检查默认数据网络是否被激活,使用同步方式返回接口,如果被激活则返回true,否则返回false。
});
// 订阅网络阻塞状态事件,当网络阻塞时,如网络性能下降、数据传输出现延迟等情况时,会触发该事件
netConnection.on('netBlockStatusChange', (data: connection.NetBlockStatusInfo) => {
logger.info("NetUtils", "NetType :" + netType + ", network netBlockStatusChange " + JSON.stringify(data));
this.postEvent(NetworkEventName.NetBlock, data, netType)
});
// 网络连接信息变化监听,当网络连接信息变化时,如从无网络到有网络、从Wi-Fi切换到蜂窝时,会触发该事件。
netConnection.on('netConnectionPropertiesChange', (data: connection.NetConnectionPropertyInfo) => {
logger.info("NetUtils",
"NetType :" + netType + ", network netConnectionPropertiesChange " + JSON.stringify(data));
this.postEvent(NetworkEventName.NetConnectionPropertiesChange, data, netType);
});
// 订阅网络丢失事件,当网络严重中断或正常断开时触发该事件
// 网络丢失是指网络严重中断或正常断开事件,当断开Wi-Fi时,是属于正常断开网络连接,会触发netLost事件
netConnection.on('netLost', (data: connection.NetHandle) => {
this.postEvent(NetworkEventName.NetLost, true, netType)
logger.info("NetUtils", "NetType :" + netType + ", Succeeded to get netLost: " + JSON.stringify(data));
});
// 订阅网络不可用事件,当网络不可用时触发该事件
// 网络不可用是指网络不可用事件,当连接的网络不能使用时,会触发netUnavailable事件。
netConnection.on('netUnavailable', () => {
logger.info("NetUtils", "NetType :" + netType + ", Succeeded to get unavailable net event");
this.postEvent(NetworkEventName.NetUnavailable, true, netType);
});
this.connectionMap.set(netType, netConnection);
}
wifiStateObserve() {
// 注册WLAN状态改变事件
// 0,未激活;1,已激活;2,激活中;3:去激活中
wifiManager.on("wifiStateChange", (result: number) => {
logger.info("NetUtils", "wifiStateChange: " + result);
this.postEvent(NetworkEventName.WifiStateChange, result);
});
// 注册WLAN连接状态改变事件
// 0,已断开;1,已连接
wifiManager.on("wifiConnectionChange", (result: number) => {
logger.info("NetUtils", "wifiConnectionChange: " + result);
this.postEvent(NetworkEventName.WifiConnectionChange, result);
});
}
parseResult(data: emitter.EventData): string {
if (data.data) {
if (!data.data.eventName) {
logger.info("parseResult data.data.eventName is undefined.")
return "";
}
} else {
logger.info("parseResult data.data is undefined.")
return "";
}
let result = "";
let name: number = (data.data)!.eventName ?? -1;
switch (name) {
case NetworkEventName.NetObserverRegister.valueOf():
result = "NetObserverRegister";
break;
case NetworkEventName.NetAvailable.valueOf():
result = "NetAvailable";
break;
case NetworkEventName.NetBlock.valueOf():
result = "NetBlock";
break;
case NetworkEventName.NetLost.valueOf():
result = "NetLost";
break;
case NetworkEventName.NetCapabilitiesChange.valueOf():
result = "NetCapabilitiesChange";
break;
case NetworkEventName.NetUnavailable.valueOf():
result = "NetUnavailable";
break;
case NetworkEventName.NetConnectionPropertiesChange.valueOf():
result = "NetConnectionPropertiesChange";
break;
case NetworkEventName.WifiStateChange.valueOf():
result = "WifiStateChange";
break;
case NetworkEventName.WifiConnectionChange.valueOf():
result = "WifiConnectionChange";
break;
case NetworkEventName.WeakNet.valueOf():
result = "WeakNet";
break;
default:
result = name.toString();
break
}
let netTemp: string = "";
let temp: number = data.data!.netType ?? -1;
if (temp === 1) {
netTemp = "WIFI";
}
if (temp === 0) {
netTemp = "CELLULAR";
}
if (temp === -1) {
netTemp = temp.toString();
}
result = result + "------" + (data.data!.status ?? -1) + "------" + netTemp;
return result;
}
sceneChangeObserve() {
try {
netQuality.on('netSceneChange', (list: Array<netQuality.NetworkScene>) => {
if (list.length > 0) {
list.forEach((networkScene) => {
// 回调信息处理
logger.info(`Succeeded receive netSceneChange info`);
if (networkScene.scene == 'weakSignal' || networkScene.scene == 'congestion') {
// 表示为弱网场景
logger.info(`The current network is weak`);
this.postEvent(NetworkEventName.WeakNet, true)
} else {
this.postEvent(NetworkEventName.WeakNet, false)
}
});
}
});
} catch (err) {
logger.error('errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
}
export class NetEventData {
eventName: NetworkEventName;
status: NetworkData;
netType: connection.NetBearType;
constructor(eventName: NetworkEventName, status: NetworkData, netType: connection.NetBearType) {
this.eventName = eventName;
this.status = status;
this.netType = netType;
}
}
(二)视频播放组件
处理网络事件,控制视频播放,同步设置状态:
import { NetEventData, NetUtils, NetworkEventName } from '../utils/NetUtils';
import { emitter } from '[@kit](/user/kit).BasicServicesKit';
import { connection } from '[@kit](/user/kit).NetworkKit';
import { logger } from '../utils/Logger';
import { Prompt } from '[@kit](/user/kit).ArkUI';
import { CellularSetting } from './CellularSetting';
// 将自动播放设置通过PersistentStorage进行本地持久化存储,避免每次打开应用都需要重新设置
PersistentStorage.persistProp('cellular_auto_play', false);
PersistentStorage.persistProp('wifi_auto_play', false);
const innerEvent: emitter.InnerEvent = {
// 左上角返回按钮点击事件传递的eventId
eventId: 6
};
[@Builder](/user/Builder)
export function PageThreeBuilder() {
CellularLink()
}
[@Component](/user/Component)
export struct CellularLink {
pathStack: NavPathStack = new NavPathStack();
private navPathStack: NavPathStack = new NavPathStack();
// 视频控制器
controller: VideoController = new VideoController();
// WI-FI自动播放
[@StorageLink](/user/StorageLink)("wifi_
更多关于HarmonyOS 鸿蒙Next开发者技术支持 - 基于 Network Kit 的网络状态监听与视频播放控制技术总结的实战教程也可以访问 https://www.itying.com/category-93-b0.html
鸿蒙Next中Network Kit提供网络状态监听能力,通过on(‘netAvailable’)和on(‘netCapabilitiesChange’)事件实时感知网络变化。视频播放控制可结合媒体会话管理,利用网络状态回调动态调整播放策略,如切换清晰度或暂停/恢复播放。需在module.json5中声明ohos.permission.INTERNET权限。具体实现依赖@ohos.net.http和@ohos.multimedia.media库的API调用。
更多关于HarmonyOS 鸿蒙Next开发者技术支持 - 基于 Network Kit 的网络状态监听与视频播放控制技术总结的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
针对您分享的基于Network Kit的网络状态监听与视频播放控制技术方案,整体实现思路清晰且符合HarmonyOS Next开发规范。以下是对关键技术的几点补充说明:
-
网络监听工具类的单例设计合理,通过emitter事件机制分发网络状态变化,能够有效区分WiFi与蜂窝网络类型。建议在netAvailable事件处理中增加网络类型验证,避免因网络切换延迟导致的状态误判。
-
视频播放组件的生命周期管理严格遵循aboutToAppear/aboutToDisappear范式,确保了监听资源的及时释放。需要注意在弱网处理时,除了提示用户外,还应实现视频码率的自适应调整以提升流畅度。
-
PersistentStorage与AppStorage的配合使用解决了状态持久化与同步问题。需要注意的是,对于频繁更新的状态数据,建议结合@Watch装饰器实现更精细的状态响应。
-
在网络类型判断中,建议增加对BEARER_ETHERNET(以太网)等网络类型的兼容处理,以增强方案的普适性。
该方案有效解决了网络状态感知、设置持久化和资源管理等核心问题,为HarmonyOS应用提供了可靠的网络自适应播放能力。