开源鸿蒙网络状态监听能力集成实战:WiFi/移动网络切换、离线缓存与重连机制

作者:maaath

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

一、引言

在移动应用开发中,网络状态的变化直接影响用户体验。无论是用户进入地铁导致的网络中断,还是从 WiFi 切换到移动数据,如何优雅地处理这些网络切换场景,是每一位开发者必须面对的问题。

本文将通过 ArkUI(方舟开发框架) 原生实现,展示如何集成网络状态监听能力,包括:

  • WiFi/移动网络切换的实时监听
  • 离线缓存机制的设计与实现
  • 自动重连策略的完整方案
  • 在开源鸿蒙模拟器上的运行验证

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────┐
│                    NetworkPage (UI层)                    │
│         实时展示网络状态、信号质量、连接类型等            │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│                 NetworkService (服务层)                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────┐ │
│  │ 网络状态监听 │  │ 离线缓存管理 │  │   重连策略管理   │ │
│  └─────────────┘  └─────────────┘  └─────────────────┘ │
└─────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────┐
│              @ohos.net.connection (网络API)              │
│         getDefaultNet / getNetCapabilities              │
└─────────────────────────────────────────────────────────┘

2.2 核心模块说明

模块 职责 关键能力
NetworkService 网络服务单例 状态管理、事件分发、离线队列、重连策略
NetworkModels 数据模型定义 枚举类型、事件结构、缓存配置
NetworkPage 界面展示层 状态卡片、质量指示器、动画效果

三、数据模型定义

首先,我们需要定义网络相关的核心数据模型。这些模型将贯穿整个网络状态管理的全过程。

// NetworkModels.ets
// 网络类型枚举
export enum NetworkType {
  NONE = 'none',
  WIFI = 'wifi',
  CELLULAR = 'cellular',
  ETHERNET = 'ethernet',
  UNKNOWN = 'unknown'
}

// 网络状态枚举
export enum NetworkState {
  CONNECTED = 'connected',
  AVAILABLE = 'available',
  DISCONNECTED = 'disconnected'
}

// 连接质量枚举
export enum ConnectionQuality {
  EXCELLENT = 'excellent',
  GOOD = 'good',
  FAIR = 'fair',
  POOR = 'poor',
  UNKNOWN = 'unknown'
}

// 网络变化原因枚举
export enum NetworkChangeReason {
  CONNECTED = 'connected',
  DISCONNECTED = 'disconnected',
  TYPE_CHANGED = 'type_changed',
  QUALITY_CHANGED = 'quality_changed',
  LOST = 'lost',
  RESTORED = 'restored'
}

网络信息模型包含了网络类型的详细属性:

// 网络信息模型
export class NetworkInfo {
  type: NetworkType = NetworkType.NONE;
  state: NetworkState = NetworkState.DISCONNECTED;
  isConnected: boolean = false;
  quality: ConnectionQuality = ConnectionQuality.UNKNOWN;
  networkName: string = '';
  connectionId: number = -1;
  linkSpeed: number = 0;
  signalStrength: number = -100;
  downloadSpeed: number = 0;
  uploadSpeed: number = 0;
  timestamp: number = 0;

  getNetworkTypeName(): string {
    switch (this.type) {
      case NetworkType.WIFI:
        return 'WiFi';
      case NetworkType.CELLULAR:
        return 'Mobile Data';
      case NetworkType.ETHERNET:
        return 'Ethernet';
      case NetworkType.NONE:
        return 'No Connection';
      default:
        return 'Unknown';
    }
  }
}

事件回调接口定义了网络状态变化时需要通知的各个方法:

// 网络事件回调接口
export interface NetworkEventCallback {
  onNetworkStateChange?(event: NetworkChangeEvent): void;
  onNetworkAvailable?(info: NetworkInfo): void;
  onNetworkUnavailable?(): void;
  onWifiConnected?(info: NetworkInfo): void;
  onWifiDisconnected?(): void;
  onCellularConnected?(info: NetworkInfo): void;
  onCellularDisconnected?(): void;
  onNetworkQualityChange?(quality: ConnectionQuality): void;
}

离线缓存和重连配置是保障用户体验的重要机制:

// 离线缓存配置
export class OfflineCacheConfig {
  maxCacheSize: number = 10 * 1024 * 1024;  // 10MB
  defaultTTL: number = 3600000;               // 1小时
  autoCleanup: boolean = true;
  cleanupInterval: number = 300000;           // 5分钟
}

// 重连策略配置(指数退避算法)
export class ReconnectionConfig {
  enabled: boolean = true;
  initialDelay: number = 1000;      // 初始延迟1秒
  maxDelay: number = 30000;         // 最大延迟30秒
  backoffMultiplier: number = 2;    // 退避倍数
  maxAttempts: number = 10;         // 最大重试次数

  calculateDelay(attempt: number): number {
    const delay = this.initialDelay * Math.pow(this.backoffMultiplier, attempt - 1);
    return Math.min(delay, this.maxDelay);
  }
}

四、网络服务核心实现

4.1 服务初始化与单例模式

采用单例模式确保全局只有一个网络服务实例,避免资源浪费和状态不一致的问题:

export class NetworkService {
  private static instance: NetworkService | null = null;
  private currentInfo: NetworkInfo = new NetworkInfo();
  private eventCallbacks: Array<NetworkEventCallback> = [];
  private isInitialized: boolean = false;

  // 离线缓存和重连相关
  private offlineCache: Map<string, OfflineCacheItem<Object>> = new Map();
  private offlineQueue: Array<PendingRequest<Object>> = [];
  private reconnectionConfig: ReconnectionConfig = new ReconnectionConfig();
  private isReconnecting: boolean = false;
  private reconnectionTimerId: number = -1;

  private constructor() {}

  static getInstance(): NetworkService {
    if (!NetworkService.instance) {
      NetworkService.instance = new NetworkService();
    }
    return NetworkService.instance;
  }

  async initialize(): Promise<void> {
    if (this.isInitialized) {
      return;
    }
    try {
      await this.checkCurrentNetwork();
      this.registerNetworkChange();
      this.isInitialized = true;
      console.info('[NetworkService] Initialized successfully');
    } catch (error) {
      console.error('[NetworkService] Failed to initialize:', error);
    }
  }
}

4.2 网络状态检查

获取当前网络状态是整个监听体系的基础。我们使用 @ohos.net.connection 模块提供的 API:

private async checkCurrentNetwork(): Promise<void> {
  try {
    // 获取默认网络句柄
    const netHandle = await connection.getDefaultNet();
    // 获取网络能力信息
    const netCapabilities = await connection.getNetCapabilities(netHandle);

    const previousInfo = this.cloneNetworkInfo(this.currentInfo);
    const newInfo = new NetworkInfo();

    // 解析承载类型判断网络类型
    const bearerTypesArray: number[] = netCapabilities.bearerTypes as number[];
    if (bearerTypesArray.includes(0)) {
      newInfo.type = NetworkType.WIFI;
      newInfo.networkName = 'WiFi';
    } else if (bearerTypesArray.includes(1)) {
      newInfo.type = NetworkType.CELLULAR;
      newInfo.networkName = 'Mobile Data';
    } else if (bearerTypesArray.includes(3)) {
      newInfo.type = NetworkType.ETHERNET;
      newInfo.networkName = 'Ethernet';
    }

    // 检查网络连接状态(12表示网络可用)
    const networkCapArray: number[] = netCapabilities.networkCap as number[];
    let isConnected = false;
    if (networkCapArray && networkCapArray.length > 0) {
      for (let i = 0; i < networkCapArray.length; i++) {
        if (networkCapArray[i] === 12) {
          isConnected = true;
          break;
        }
      }
    }

    newInfo.isConnected = isConnected;
    newInfo.state = isConnected ? NetworkState.CONNECTED : NetworkState.DISCONNECTED;
    newInfo.quality = this.estimateConnectionQuality(newInfo.type);
    newInfo.connectionId = netHandle.netId;
    newInfo.timestamp = Date.now();

    this.currentInfo = newInfo;
    this.notifyNetworkChange(previousInfo, newInfo, NetworkChangeReason.CONNECTED);

  } catch (error) {
    // 网络不可用时的处理
    this.currentInfo = new NetworkInfo();
    this.currentInfo.isConnected = false;
    this.currentInfo.state = NetworkState.DISCONNECTED;
    this.notifyNetworkChange(previousInfo, this.currentInfo, NetworkChangeReason.DISCONNECTED);
  }
}

4.3 网络状态监听注册

使用 NetConnection 实现网络变化的实时监听:

private registerNetworkChange(): void {
  try {
    const netConnection = connection.createNetConnection();
    netConnection.register((registerError: Error) => {
      if (registerError) {
        console.error('[NetworkService] Failed to register:', registerError);
        return;
      }
      // 监听网络不可用事件
      netConnection.on('netUnavailable', () => {
        console.info('[NetworkService] Net unavailable');
        this.handleNetLost();
      });
      console.info('[NetworkService] Network change listener registered');
    });
  } catch (error) {
    console.error('[NetworkService] Failed to register network change:', error);
  }
}

private handleNetLost(): void {
  const previousInfo = this.cloneNetworkInfo(this.currentInfo);
  this.currentInfo = new NetworkInfo();
  this.currentInfo.isConnected = false;
  this.currentInfo.state = NetworkState.DISCONNECTED;
  this.currentInfo.timestamp = Date.now();

  this.notifyNetworkChange(previousInfo, this.currentInfo, NetworkChangeReason.LOST);
  this.notifyNetworkUnavailable();

  if (this.reconnectionConfig.enabled) {
    this.startReconnection();
  }
}

4.4 事件通知机制

采用观察者模式,通过回调接口向订阅者分发网络状态变化事件:

private notifyNetworkChange(
  previous: NetworkInfo,
  current: NetworkInfo,
  reason: NetworkChangeReason
): void {
  const event = new NetworkChangeEvent(previous, current, reason);
  for (let i = 0; i < this.eventCallbacks.length; i++) {
    const callback = this.eventCallbacks[i];
    if (callback.onNetworkStateChange) {
      callback.onNetworkStateChange(event);
    }

    // 针对不同网络类型的特定通知
    if (current.type === NetworkType.WIFI) {
      if (current.isConnected && callback.onWifiConnected) {
        callback.onWifiConnected(current);
      } else if (!current.isConnected && callback.onWifiDisconnected) {
        callback.onWifiDisconnected();
      }
    }

    if (current.type === NetworkType.CELLULAR) {
      if (current.isConnected && callback.onCellularConnected) {
        callback.onCellularConnected(current);
      } else if (!current.isConnected && callback.onCellularDisconnected) {
        callback.onCellularDisconnected();
      }
    }
  }
}

4.5 离线缓存管理

当网络不可用时,缓存机制确保关键数据不会丢失:

// 缓存数据到本地
cacheData(key: string, data: Object, ttlMs?: number): void {
  const ttl = ttlMs ?? this.cacheConfig.defaultTTL;
  const item = new OfflineCacheItem(key, data, ttl);
  this.offlineCache.set(key, item);
}

// 获取缓存数据(自动检查过期)
getCachedData(key: string): Object | null {
  const item = this.offlineCache.get(key);
  if (!item) {
    return null;
  }
  if (item.checkExpiration()) {
    this.offlineCache.delete(key);
    return null;
  }
  return item.data;
}

4.6 指数退避重连策略

使用指数退避算法实现智能重连,避免频繁请求造成服务器压力:

private startReconnection(): void {
  if (this.isReconnecting) {
    return;
  }
  this.isReconnecting = true;
  this.reconnectionAttempts = 0;
  this.scheduleReconnection();
}

private scheduleReconnection(): void {
  // 达到最大重试次数后停止
  if (!this.isReconnecting ||
      this.reconnectionAttempts >= this.reconnectionConfig.maxAttempts) {
    this.cancelReconnection();
    return;
  }

  this.reconnectionAttempts++;
  // 指数退避计算延迟时间
  const delay = this.reconnectionConfig.calculateDelay(this.reconnectionAttempts);

  this.reconnectionTimerId = setTimeout(() => {
    this.checkCurrentNetwork().then(() => {
      if (this.isNetworkConnected()) {
        this.cancelReconnection();
        console.info('[NetworkService] Reconnection successful');
        this.processOfflineQueue();  // 重连成功后处理离线队列
      } else {
        this.scheduleReconnection();  // 继续重连
      }
    });
  }, delay) as number;
}

五、UI 界面实现

5.1 页面结构设计

界面采用卡片式布局,清晰展示网络状态的核心信息:

@Entry
@Component
struct NetworkPage {
  @State netState: NetDisplayState = new NetDisplayState();
  @State headerOpacity: number = 0;
  @State cardOpacity: number = 0;
  @State cardTranslateY: number = 30;
  @State qualityBars: number[] = [0, 0, 0, 0];

  private networkService: NetworkService = NetworkService.getInstance();
  private callbackImpl: NetworkEventCallbackImpl | null = null;

  aboutToAppear(): void {
    this.animateEntrance();
    this.initNetwork();
  }

  aboutToDisappear(): void {
    if (this.callbackImpl) {
      this.networkService.unregisterEventCallback(this.callbackImpl);
    }
  }
}

5.2 网络事件回调实现

页面需要实现 NetworkEventCallback 接口来接收网络状态变化通知:

class NetworkEventCallbackImpl implements NetworkEventCallback {
  private pageRef: NetworkPage;

  constructor(page: NetworkPage) {
    this.pageRef = page;
  }

  onNetworkStateChange(event: NetworkChangeEvent): void {
    this.pageRef.updateFromInfo(event.currentInfo);
    this.pageRef.triggerPulse();
  }

  onNetworkAvailable(info: NetworkInfo): void {
    this.pageRef.updateFromInfo(info);
  }

  onNetworkUnavailable(): void {
    this.pageRef.netState.connected = false;
    this.pageRef.netState.type = NetworkType.NONE;
    this.pageRef.netState.typeName = 'No Connection';
    this.pageRef.netState.isReconnecting = true;
  }

  onNetworkQualityChange(quality: ConnectionQuality): void {
    this.pageRef.netState.quality = quality;
    this.pageRef.netState.qualityText = this.getQualityText(quality);
    this.pageRef.updateQualityBars(quality);
  }
}

5.3 网络状态卡片组件

@Builder
NetworkStatusCard() {
  Column() {
    // 状态图标
    Image(this.netState.connected ?
      (this.netState.type === NetworkType.WIFI ? $r('app.media.icon_wifi') : $r('app.media.icon_cellular')) :
      $r('app.media.icon_offline'))
      .width(64)
      .height(64)
      .fillColor(this.getStatusColor())

    // 状态文字
    Text(this.netState.connected ? 'Connected' : 'Offline')
      .fontSize(24)
      .fontWeight(FontWeight.Bold)
      .margin({ top: 16 })

    // 网络类型
    Text(this.netState.typeName)
      .fontSize(16)
      .fontColor('#666666')
      .margin({ top: 8 })

    // 重连状态指示
    if (this.netState.isReconnecting) {
      Text(`Reconnecting... (${this.netState.reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`)
        .fontSize(14)
        .fontColor('#FF9800')
        .margin({ top: 8 })
    }
  }
  .width('100%')
  .padding(24)
  .backgroundColor('#FFFFFF')
  .borderRadius(16)
}

六、运行验证

6.1 模拟器环境准备

  1. 打开 DevEco Studio,选择 OpenHarmony 项目
  2. 配置模拟器或连接真实设备
  3. 确保设备已开启网络权限

6.2 运行效果展示

应用启动后,界面会显示当前的网络状态:

连接状态展示(离线):
联网后即可看到:

  • 顶部卡片显示连接状态(Connected/Offline)
  • 中间显示网络类型图标(WiFi/移动数据/以太网)
  • 底部显示信号质量指示条

在这里插入图片描述

6.3 关键日志输出

# 初始化成功
[NetworkService] Initialized successfully
[NetworkService] Network change listener registered

# 网络连接
[NetworkService] Network change: connected -> wifi

# 网络断开
[NetworkService] Net unavailable
[NetworkService] Reconnection scheduled, attempt: 1

# 重连成功
[NetworkService] Reconnection successful

七、实践建议

7.1 性能优化建议

  1. 避免频繁查询:网络状态检查不宜过于频繁,建议间隔 1-2 秒
  2. 合理使用缓存:根据数据类型设置合适的 TTL,避免内存浪费
  3. 资源及时释放:页面销毁时务必取消监听和定时器

7.2 稳定性保障

  1. 超时处理:所有网络操作都应设置合理的超时时间
  2. 错误恢复:提供手动重连入口,避免完全依赖自动重连
  3. 状态持久化:关键状态可考虑持久化存储

7.3 用户体验优化

  1. 渐进式反馈:网络变化时提供视觉提示,而非静默更新
  2. 优雅降级:离线时展示缓存数据,提升可用性感知
  3. 后台处理:离线队列的请求应在后台静默处理

八、总结

本文详细介绍了在开源鸿蒙平台上实现网络状态监听能力的完整方案,包括:

  • 网络状态实时监听:通过 @ohos.net.connection API 监听 WiFi/移动网络切换
  • 离线缓存机制:在网络不可用时缓存关键数据,保障应用可用性
  • 智能重连策略:采用指数退避算法,实现稳定可靠的网络恢复
  • 优雅 UI 反馈:通过动画效果和状态卡片,提供流畅的用户体验

感谢各位阅读!


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐