HarmonyOS APP开发中网络状态监听:在线/离线检测

让你的应用拥有网络感知能力,优雅应对各种网络环境

一、为什么需要网络状态监听?

用户打开你的应用,发现数据加载不出来,是网络问题还是服务器问题?用户切换了WiFi和4G,应用是否需要重新请求?用户走进电梯信号丢失,应用如何优雅降级?

这些问题的答案都指向同一个需求:网络状态监听。应用需要实时感知网络环境的变化,并做出相应的响应。

网络状态监听要解决的核心问题:

用户体验优化:网络断开时,不要让用户盯着加载圈发呆,而是给出明确提示。网络恢复时,自动重试未完成的请求。

智能请求策略:WiFi环境下可以下载大文件,移动网络下提示用户注意流量。弱网环境下降低请求频率或请求小图。

资源管理:网络不可用时暂停实时推送、停止轮询,节省电量和流量。

故障排查:记录网络状态变化日志,帮助定位用户反馈的问题。

鸿蒙系统提供了@ohos.net.connection模块,可以全面监听网络状态变化。

二、核心原理:网络状态监听机制

2.1 网络状态监听流程

网络可用

网络丢失

网络变更

连接属性变化

应用启动

注册网络监听

获取初始网络状态

网络可用?

执行在线逻辑

执行离线逻辑

等待网络变化

网络变化事件

触发onNetAvailable

触发onNetLost

触发onNetChange

触发onNetConnectionPropertiesChanged

更新状态: 在线

更新状态: 离线

更新网络类型

更新连接属性

通知业务层

2.2 网络类型与连接状态

网络类型 说明 典型场景
WIFI WiFi连接 家庭、办公室
CELLULAR 蜂窝网络 4G/5G移动网络
ETHERNET 以太网 有线连接设备
BLUETOOTH 蓝牙网络 蓝牙共享网络
VPN VPN连接 安全访问内网
连接状态 说明
UNKNOWN 未知状态
CONNECTED 已连接
DISCONNECTED 已断开
CONNECTING 连接中
DISCONNECTING 断开中

2.3 鸿蒙网络连接API

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

// 创建网络连接监听器
let netConnection = connection.createNetConnection();

// 注册监听
netConnection.on('netAvailable', (data) => {
  console.info('网络可用:', data.netHandle.netId);
});

netConnection.on('netLost', (data) => {
  console.info('网络丢失:', data.netHandle.netId);
});

// 获取当前网络状态
let netHandle = await connection.getDefaultNet();
let capabilities = await connection.getNetCapabilities(netHandle);

三、代码实战:三种典型场景

场景一:基础网络状态监听

实现完整的网络状态监听,包括在线/离线检测、网络类型识别。

import connection from '@ohos.net.connection';
import { BusinessError } from '@ohos.base';

// 网络状态信息
interface NetworkStatus {
  isConnected: boolean;        // 是否连接
  networkType: NetworkType;    // 网络类型
  isMetered: boolean;          // 是否计费网络
  signalStrength: number;      // 信号强度(0-100)
  ssid?: string;               // WiFi名称
  lastUpdate: number;          // 最后更新时间
}

// 网络类型枚举
enum NetworkType {
  UNKNOWN = 'unknown',
  WIFI = 'wifi',
  CELLULAR_4G = '4g',
  CELLULAR_5G = '5g',
  ETHERNET = 'ethernet',
  BLUETOOTH = 'bluetooth',
  VPN = 'vpn'
}

// 网络状态管理器
class NetworkStatusManager {
  private netConnection: connection.NetConnection | null = null;
  private currentStatus: NetworkStatus;
  
  // 回调
  private onStatusChange: ((status: NetworkStatus) => void) | null = null;
  private onOnline: (() => void) | null = null;
  private onOffline: (() => void) | null = null;
  
  constructor() {
    // 初始化状态
    this.currentStatus = {
      isConnected: false,
      networkType: NetworkType.UNKNOWN,
      isMetered: true,
      signalStrength: 0,
      lastUpdate: Date.now()
    };
  }
  
  // 启动监听
  async start(): Promise<void> {
    // 创建网络连接监听器
    this.netConnection = connection.createNetConnection();
  
    // 注册监听事件
    this.setupListeners();
  
    // 获取初始状态
    await this.updateCurrentStatus();
  
    console.info('网络状态监听已启动');
  }
  
  // 设置监听
  private setupListeners(): void {
    if (!this.netConnection) return;
  
    // 网络可用
    this.netConnection.on('netAvailable', async (data: connection.NetHandle) => {
      console.info('网络可用:', data.netId);
      await this.updateCurrentStatus();
    
      // 触发上线回调
      if (!this.currentStatus.isConnected) {
        this.currentStatus.isConnected = true;
        this.onOnline?.();
      }
    
      this.notifyStatusChange();
    });
  
    // 网络丢失
    this.netConnection.on('netLost', (data: connection.NetHandle) => {
      console.info('网络丢失:', data.netId);
    
      this.currentStatus = {
        isConnected: false,
        networkType: NetworkType.UNKNOWN,
        isMetered: true,
        signalStrength: 0,
        lastUpdate: Date.now()
      };
    
      this.onOffline?.();
      this.notifyStatusChange();
    });
  
    // 网络变更(切换网络)
    this.netConnection.on('netChange', async (data: connection.NetHandle) => {
      console.info('网络变更:', data.netId);
      await this.updateCurrentStatus();
      this.notifyStatusChange();
    });
  
    // 连接属性变化
    this.netConnection.on('netConnectionPropertiesChanged', async (data: connection.ConnectionProperties) => {
      console.info('连接属性变化');
      await this.updateCurrentStatus();
      this.notifyStatusChange();
    });
  
    // 网络能力变化
    this.netConnection.on('netCapabilitiesChange', async (data: connection.NetCapabilityInfo) => {
      console.info('网络能力变化');
      await this.updateCurrentStatus();
      this.notifyStatusChange();
    });
  }
  
  // 更新当前状态
  private async updateCurrentStatus(): Promise<void> {
    try {
      // 获取默认网络
      let netHandle = await connection.getDefaultNet();
    
      if (!netHandle || netHandle.netId === 0) {
        // 无网络连接
        this.currentStatus.isConnected = false;
        this.currentStatus.networkType = NetworkType.UNKNOWN;
        this.currentStatus.lastUpdate = Date.now();
        return;
      }
    
      // 获取网络能力
      let capabilities = await connection.getNetCapabilities(netHandle);
    
      // 解析网络类型
      let networkType = this.parseNetworkType(capabilities);
    
      // 判断是否计费网络
      let isMetered = this.checkIfMetered(capabilities);
    
      // 获取信号强度(如果支持)
      let signalStrength = await this.getSignalStrength();
    
      // 获取WiFi SSID(如果是WiFi)
      let ssid: string | undefined;
      if (networkType === NetworkType.WIFI) {
        ssid = await this.getWifiSsid();
      }
    
      this.currentStatus = {
        isConnected: true,
        networkType: networkType,
        isMetered: isMetered,
        signalStrength: signalStrength,
        ssid: ssid,
        lastUpdate: Date.now()
      };
    
    } catch (error) {
      console.error('更新网络状态失败:', error);
    
      // 出错时假设无网络
      this.currentStatus.isConnected = false;
      this.currentStatus.lastUpdate = Date.now();
    }
  }
  
  // 解析网络类型
  private parseNetworkType(capabilities: connection.NetCapabilities): NetworkType {
    // 检查是否是WiFi
    if (capabilities.bearerTypes.includes(connection.BearerType.BEARER_WIFI)) {
      return NetworkType.WIFI;
    }
  
    // 检查是否是蜂窝网络
    if (capabilities.bearerTypes.includes(connection.BearerType.BEARER_CELLULAR)) {
      // 进一步判断是4G还是5G
      // 简化处理,返回4G
      return NetworkType.CELLULAR_4G;
    }
  
    // 检查是否是以太网
    if (capabilities.bearerTypes.includes(connection.BearerType.BEARER_ETHERNET)) {
      return NetworkType.ETHERNET;
    }
  
    // 检查是否是蓝牙
    if (capabilities.bearerTypes.includes(connection.BearerType.BEARER_BLUETOOTH)) {
      return NetworkType.BLUETOOTH;
    }
  
    return NetworkType.UNKNOWN;
  }
  
  // 判断是否计费网络
  private checkIfMetered(capabilities: connection.NetCapabilities): boolean {
    // WiFi和以太网通常不计费
    if (capabilities.bearerTypes.includes(connection.BearerType.BEARER_WIFI) ||
        capabilities.bearerTypes.includes(connection.BearerType.BEARER_ETHERNET)) {
      return false;
    }
  
    // 蜂窝网络计费
    return true;
  }
  
  // 获取信号强度
  private async getSignalStrength(): Promise<number> {
    // 简化处理,返回固定值
    // 实际需要调用系统API获取
    return 80;
  }
  
  // 获取WiFi SSID
  private async getWifiSsid(): Promise<string | undefined> {
    // 简化处理
    // 实际需要调用WiFi API获取
    return 'MyWiFi';
  }
  
  // 通知状态变化
  private notifyStatusChange(): void {
    console.info('网络状态更新:', JSON.stringify(this.currentStatus));
    this.onStatusChange?.(this.currentStatus);
  }
  
  // 停止监听
  stop(): void {
    if (this.netConnection) {
      // 取消所有监听
      this.netConnection.off('netAvailable');
      this.netConnection.off('netLost');
      this.netConnection.off('netChange');
      this.netConnection.off('netConnectionPropertiesChanged');
      this.netConnection.off('netCapabilitiesChange');
    
      this.netConnection = null;
    }
  
    console.info('网络状态监听已停止');
  }
  
  // 获取当前状态
  getCurrentStatus(): NetworkStatus {
    return { ...this.currentStatus };
  }
  
  // 判断是否在线
  isOnline(): boolean {
    return this.currentStatus.isConnected;
  }
  
  // 判断是否是WiFi
  isWifi(): boolean {
    return this.currentStatus.networkType === NetworkType.WIFI;
  }
  
  // 判断是否是移动网络
  isMobile(): boolean {
    return this.currentStatus.networkType === NetworkType.CELLULAR_4G ||
           this.currentStatus.networkType === NetworkType.CELLULAR_5G;
  }
  
  // 设置回调
  setOnStatusChange(callback: (status: NetworkStatus) => void): void {
    this.onStatusChange = callback;
  }
  
  setOnOnline(callback: () => void): void {
    this.onOnline = callback;
  }
  
  setOnOffline(callback: () => void): void {
    this.onOffline = callback;
  }
}

// 网络状态显示组件
@Entry
@Component
struct NetworkStatusPage {
  @State networkStatus: NetworkStatus | null = null;
  @State statusHistory: string[] = [];
  
  private networkManager: NetworkStatusManager = new NetworkStatusManager();
  
  async aboutToAppear() {
    // 设置回调
    this.networkManager.setOnStatusChange((status) => {
      this.networkStatus = status;
      this.addHistory(`状态更新: ${status.isConnected ? '在线' : '离线'} (${status.networkType})`);
    });
  
    this.networkManager.setOnOnline(() => {
      this.addHistory('网络已连接');
    });
  
    this.networkManager.setOnOffline(() => {
      this.addHistory('网络已断开');
    });
  
    // 启动监听
    await this.networkManager.start();
  
    // 获取初始状态
    this.networkStatus = this.networkManager.getCurrentStatus();
  }
  
  aboutToDisappear() {
    this.networkManager.stop();
  }
  
  private addHistory(message: string) {
    let time = new Date().toLocaleTimeString();
    this.statusHistory.unshift(`[${time}] ${message}`);
    if (this.statusHistory.length > 20) {
      this.statusHistory.pop();
    }
  }
  
  build() {
    Column() {
      // 网络状态卡片
      if (this.networkStatus) {
        this.StatusCard()
      }
    
      // 状态历史
      Column() {
        Text('状态变化历史:')
          .fontSize(14)
          .width('100%')
      
        List() {
          ForEach(this.statusHistory, (log: string, index: number) => {
            ListItem() {
              Text(log)
                .fontSize(12)
                .fontFamily('monospace')
            }
          })
        }
        .width('100%')
        .layoutWeight(1)
        .backgroundColor('#F5F5F5')
        .padding(10)
      }
      .width('100%')
      .padding(10)
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
  
  @Builder
  StatusCard() {
    Column() {
      // 连接状态
      Row() {
        Circle()
          .width(20)
          .height(20)
          .fill(this.networkStatus?.isConnected ? '#7ED321' : '#D0021B')
      
        Text(this.networkStatus?.isConnected ? '已连接' : '未连接')
          .fontSize(18)
          .fontWeight(FontWeight.Bold)
          .margin({ left: 10 })
      }
      .width('100%')
    
      Divider().margin({ top: 15, bottom: 15 })
    
      // 网络类型
      Row() {
        Text('网络类型:')
          .fontSize(14)
          .layoutWeight(1)
      
        Text(this.getNetworkTypeText())
          .fontSize(14)
          .fontColor('#4A90E2')
      }
      .width('100%')
    
      // 是否计费
      Row() {
        Text('计费网络:')
          .fontSize(14)
          .layoutWeight(1)
      
        Text(this.networkStatus?.isMetered ? '是' : '否')
          .fontSize(14)
          .fontColor(this.networkStatus?.isMetered ? '#F5A623' : '#7ED321')
      }
      .width('100%')
      .margin({ top: 10 })
    
      // 信号强度
      if (this.networkStatus?.isConnected) {
        Row() {
          Text('信号强度:')
            .fontSize(14)
            .layoutWeight(1)
        
          Row() {
            Stack() {
              Row()
                .width(100)
                .height(10)
                .backgroundColor('#E8E8E8')
                .borderRadius(5)
            
              Row()
                .width(`${this.networkStatus?.signalStrength}%`)
                .height(10)
                .backgroundColor('#4A90E2')
                .borderRadius(5)
            }
            .width(100)
            .height(10)
          
            Text(`${this.networkStatus?.signalStrength}%`)
              .fontSize(12)
              .margin({ left: 10 })
          }
        }
        .width('100%')
        .margin({ top: 10 })
      }
    
      // WiFi SSID
      if (this.networkStatus?.ssid) {
        Row() {
          Text('WiFi名称:')
            .fontSize(14)
            .layoutWeight(1)
        
          Text(this.networkStatus?.ssid)
            .fontSize(14)
            .fontColor('#666')
        }
        .width('100%')
        .margin({ top: 10 })
      }
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#FFFFFF')
    .borderRadius(8)
    .margin(10)
  }
  
  private getNetworkTypeText(): string {
    switch (this.networkStatus?.networkType) {
      case NetworkType.WIFI:
        return 'WiFi';
      case NetworkType.CELLULAR_4G:
        return '4G';
      case NetworkType.CELLULAR_5G:
        return '5G';
      case NetworkType.ETHERNET:
        return '以太网';
      case NetworkType.BLUETOOTH:
        return '蓝牙';
      default:
        return '未知';
    }
  }
}

场景二:智能请求策略

根据网络状态自动调整请求策略。

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

// 请求策略配置
interface RequestStrategy {
  maxImageSize: number;       // 最大图片尺寸
  requestTimeout: number;     // 请求超时
  retryCount: number;         // 重试次数
  retryDelay: number;         // 重试延迟
  enablePreload: boolean;     // 是否预加载
  enableBackgroundSync: boolean;  // 是否后台同步
}

// 默认策略
const STRATEGIES = {
  // WiFi策略:无限制
  WIFI: {
    maxImageSize: 4096,
    requestTimeout: 30000,
    retryCount: 3,
    retryDelay: 1000,
    enablePreload: true,
    enableBackgroundSync: true
  } as RequestStrategy,
  
  // 4G策略:适度限制
  CELLULAR_4G: {
    maxImageSize: 2048,
    requestTimeout: 20000,
    retryCount: 2,
    retryDelay: 2000,
    enablePreload: false,
    enableBackgroundSync: false
  } as RequestStrategy,
  
  // 5G策略:接近WiFi
  CELLULAR_5G: {
    maxImageSize: 3072,
    requestTimeout: 25000,
    retryCount: 3,
    retryDelay: 1500,
    enablePreload: true,
    enableBackgroundSync: false
  } as RequestStrategy,
  
  // 离线策略
  OFFLINE: {
    maxImageSize: 0,
    requestTimeout: 0,
    retryCount: 0,
    retryDelay: 0,
    enablePreload: false,
    enableBackgroundSync: false
  } as RequestStrategy
};

// 智能网络请求管理器
class SmartNetworkManager {
  private networkManager: NetworkStatusManager;
  private currentStrategy: RequestStrategy;
  private pendingRequests: Array<() => Promise<void>> = [];
  
  // 回调
  private onStrategyChange: ((strategy: RequestStrategy) => void) | null = null;
  
  constructor(networkManager: NetworkStatusManager) {
    this.networkManager = networkManager;
    this.currentStrategy = STRATEGIES.OFFLINE;
  
    // 监听网络状态变化
    this.networkManager.setOnStatusChange((status) => {
      this.updateStrategy(status);
    });
  }
  
  // 更新策略
  private updateStrategy(status: NetworkStatus): void {
    let newStrategy: RequestStrategy;
  
    if (!status.isConnected) {
      newStrategy = STRATEGIES.OFFLINE;
    } else {
      switch (status.networkType) {
        case NetworkType.WIFI:
          newStrategy = STRATEGIES.WIFI;
          break;
        case NetworkType.CELLULAR_4G:
          newStrategy = STRATEGIES.CELLULAR_4G;
          break;
        case NetworkType.CELLULAR_5G:
          newStrategy = STRATEGIES.CELLULAR_5G;
          break;
        default:
          newStrategy = STRATEGIES.CELLULAR_4G;  // 保守策略
      }
    }
  
    // 检查策略是否变化
    if (JSON.stringify(newStrategy) !== JSON.stringify(this.currentStrategy)) {
      this.currentStrategy = newStrategy;
      console.info('请求策略已更新:', JSON.stringify(newStrategy));
    
      this.onStrategyChange?.(newStrategy);
    
      // 网络恢复时,执行待处理请求
      if (status.isConnected && this.pendingRequests.length > 0) {
        this.executePendingRequests();
      }
    }
  }
  
  // 执行待处理请求
  private async executePendingRequests(): Promise<void> {
    console.info(`执行 ${this.pendingRequests.length} 个待处理请求`);
  
    let requests = [...this.pendingRequests];
    this.pendingRequests = [];
  
    for (let request of requests) {
      try {
        await request();
      } catch (error) {
        console.error('待处理请求执行失败:', error);
      }
    }
  }
  
  // 执行请求(智能策略)
  async executeRequest<T>(
    request: () => Promise<T>,
    options: {
      offlineBehavior?: 'reject' | 'queue' | 'cache';  // 离线行为
      requireWifi?: boolean;  // 是否要求WiFi
    } = {}
  ): Promise<T | null> {
    let status = this.networkManager.getCurrentStatus();
  
    // 检查网络
    if (!status.isConnected) {
      switch (options.offlineBehavior || 'reject') {
        case 'reject':
          throw new Error('网络不可用');
        
        case 'queue':
          // 加入待处理队列
          return new Promise((resolve) => {
            this.pendingRequests.push(async () => {
              let result = await request();
              resolve(result);
            });
          });
        
        case 'cache':
          // 返回缓存数据
          return null;
      }
    }
  
    // 检查WiFi要求
    if (options.requireWifi && status.networkType !== NetworkType.WIFI) {
      throw new Error('此操作需要WiFi网络');
    }
  
    // 执行请求(带重试)
    let retryCount = this.currentStrategy.retryCount;
    let lastError: Error | null = null;
  
    for (let i = 0; i <= retryCount; i++) {
      try {
        return await request();
      } catch (error) {
        lastError = error as Error;
      
        if (i < retryCount) {
          // 等待重试
          await this.delay(this.currentStrategy.retryDelay * (i + 1));
        }
      }
    }
  
    throw lastError;
  }
  
  // 获取图片URL(根据网络调整尺寸)
  getImageUrl(originalUrl: string): string {
    let maxSize = this.currentStrategy.maxImageSize;
  
    if (maxSize === 0) {
      // 离线,返回占位图
      return 'asset:///placeholder.png';
    }
  
    // 根据最大尺寸调整URL
    // 假设图片服务支持尺寸参数
    return `${originalUrl}?size=${maxSize}`;
  }
  
  // 判断是否可以预加载
  canPreload(): boolean {
    return this.currentStrategy.enablePreload;
  }
  
  // 判断是否可以后台同步
  canBackgroundSync(): boolean {
    return this.currentStrategy.enableBackgroundSync;
  }
  
  // 获取当前策略
  getCurrentStrategy(): RequestStrategy {
    return { ...this.currentStrategy };
  }
  
  // 设置回调
  setOnStrategyChange(callback: (strategy: RequestStrategy) => void): void {
    this.onStrategyChange = callback;
  }
  
  // 延迟工具
  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// 使用示例
@Entry
@Component
struct SmartRequestPage {
  @State strategyInfo: string = '';
  @State networkStatus: NetworkStatus | null = null;
  
  private networkManager: NetworkStatusManager = new NetworkStatusManager();
  private smartManager: SmartNetworkManager | null = null;
  
  async aboutToAppear() {
    await this.networkManager.start();
  
    this.smartManager = new SmartNetworkManager(this.networkManager);
  
    this.smartManager.setOnStrategyChange((strategy) => {
      this.strategyInfo = JSON.stringify(strategy, null, 2);
    });
  
    this.networkManager.setOnStatusChange((status) => {
      this.networkStatus = status;
    });
  
    this.networkStatus = this.networkManager.getCurrentStatus();
    this.strategyInfo = JSON.stringify(this.smartManager.getCurrentStrategy(), null, 2);
  }
  
  aboutToDisappear() {
    this.networkManager.stop();
  }
  
  // 测试请求
  async testRequest() {
    try {
      let result = await this.smartManager?.executeRequest(
        async () => {
          // 模拟请求
          await new Promise(resolve => setTimeout(resolve, 1000));
          return { data: 'success' };
        },
        { offlineBehavior: 'queue' }
      );
    
      console.info('请求成功:', result);
    } catch (error) {
      console.error('请求失败:', error);
    }
  }
  
  build() {
    Column() {
      // 网络状态
      Row() {
        Text('网络状态:')
          .fontSize(16)
        Text(this.networkStatus?.isConnected ? '在线' : '离线')
          .fontSize(16)
          .fontColor(this.networkStatus?.isConnected ? '#7ED321' : '#D0021B')
          .margin({ left: 10 })
      }
      .width('100%')
      .padding(15)
    
      // 当前策略
      Column() {
        Text('当前请求策略:')
          .fontSize(14)
          .width('100%')
      
        Text(this.strategyInfo)
          .fontSize(12)
          .fontFamily('monospace')
      }
      .width('100%')
      .padding(10)
      .backgroundColor('#F5F5F5')
    
      // 测试按钮
      Button('测试请求')
        .width('100%')
        .margin({ top: 20 })
        .onClick(() => this.testRequest())
    }
    .width('100%')
    .padding(20)
  }
}

场景三:离线缓存与同步

实现离线数据缓存和网络恢复后自动同步。

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

// 离线数据项
interface OfflineDataItem {
  id: string;
  type: string;
  data: any;
  timestamp: number;
  synced: boolean;
}

// 离线数据管理器
class OfflineDataManager {
  private storage: preferences.Preferences | null = null;
  private pendingSync: OfflineDataItem[] = [];
  private networkManager: NetworkStatusManager;
  private isSyncing: boolean = false;
  
  // 回调
  private onSyncStart: (() => void) | null = null;
  private onSyncComplete: ((count: number) => void) | null = null;
  private onSyncError: ((error: Error) => void) | null = null;
  
  constructor(networkManager: NetworkStatusManager) {
    this.networkManager = networkManager;
  
    // 监听网络恢复
    this.networkManager.setOnOnline(() => {
      this.syncPendingData();
    });
  }
  
  // 初始化
  async init(context: Context): Promise<void> {
    this.storage = await preferences.getPreferences(context, 'offline_data');
  
    // 加载待同步数据
    await this.loadPendingData();
  }
  
  // 加载待同步数据
  private async loadPendingData(): Promise<void> {
    if (!this.storage) return;
  
    try {
      let dataStr = await this.storage.get('pending_sync', '[]') as string;
      this.pendingSync = JSON.parse(dataStr);
    
      console.info(`加载 ${this.pendingSync.length} 条待同步数据`);
    } catch (error) {
      this.pendingSync = [];
    }
  }
  
  // 保存待同步数据
  private async savePendingData(): Promise<void> {
    if (!this.storage) return;
  
    await this.storage.put('pending_sync', JSON.stringify(this.pendingSync));
    await this.storage.flush();
  }
  
  // 保存数据(离线可用)
  async saveData(type: string, data: any): Promise<string> {
    let item: OfflineDataItem = {
      id: `offline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
      type: type,
      data: data,
      timestamp: Date.now(),
      synced: false
    };
  
    // 检查网络状态
    if (this.networkManager.isOnline()) {
      // 在线,直接同步
      try {
        await this.syncItem(item);
        item.synced = true;
        console.info('数据已同步:', item.id);
      } catch (error) {
        // 同步失败,加入待同步队列
        this.pendingSync.push(item);
        await this.savePendingData();
        console.warn('同步失败,已加入待同步队列:', item.id);
      }
    } else {
      // 离线,加入待同步队列
      this.pendingSync.push(item);
      await this.savePendingData();
      console.info('离线保存,已加入待同步队列:', item.id);
    }
  
    return item.id;
  }
  
  // 同步单个数据项
  private async syncItem(item: OfflineDataItem): Promise<void> {
    // 根据类型调用不同的同步API
    switch (item.type) {
      case 'user_profile':
        // await api.updateUserProfile(item.data);
        break;
      case 'order':
        // await api.createOrder(item.data);
        break;
      case 'message':
        // await api.sendMessage(item.data);
        break;
      default:
        throw new Error(`未知数据类型: ${item.type}`);
    }
  }
  
  // 同步待处理数据
  async syncPendingData(): Promise<void> {
    if (this.isSyncing) {
      console.warn('同步进行中,跳过');
      return;
    }
  
    if (this.pendingSync.length === 0) {
      return;
    }
  
    if (!this.networkManager.isOnline()) {
      console.warn('网络不可用,跳过同步');
      return;
    }
  
    this.isSyncing = true;
    this.onSyncStart?.();
  
    console.info(`开始同步 ${this.pendingSync.length} 条数据`);
  
    let successCount = 0;
    let failedItems: OfflineDataItem[] = [];
  
    for (let item of this.pendingSync) {
      try {
        await this.syncItem(item);
        successCount++;
        console.info(`同步成功: ${item.id}`);
      } catch (error) {
        console.error(`同步失败: ${item.id}`, error);
        failedItems.push(item);
      }
    }
  
    // 更新待同步列表
    this.pendingSync = failedItems;
    await this.savePendingData();
  
    this.isSyncing = false;
  
    if (successCount > 0) {
      this.onSyncComplete?.(successCount);
    }
  
    if (failedItems.length > 0) {
      this.onSyncError?.(new Error(`${failedItems.length} 条数据同步失败`));
    }
  }
  
  // 获取待同步数量
  getPendingCount(): number {
    return this.pendingSync.length;
  }
  
  // 清除所有待同步数据
  async clearAll(): Promise<void> {
    this.pendingSync = [];
    await this.savePendingData();
  }
  
  // 设置回调
  setOnSyncStart(callback: () => void): void {
    this.onSyncStart = callback;
  }
  
  setOnSyncComplete(callback: (count: number) => void): void {
    this.onSyncComplete = callback;
  }
  
  setOnSyncError(callback: (error: Error) => void): void {
    this.onSyncError = callback;
  }
}

// 离线数据管理页面
@Entry
@Component
struct OfflineDataPage {
  @State pendingCount: number = 0;
  @State syncStatus: string = '';
  @State logs: string[] = [];
  
  private networkManager: NetworkStatusManager = new NetworkStatusManager();
  private offlineManager: OfflineDataManager | null = null;
  
  async aboutToAppear() {
    await this.networkManager.start();
  
    this.offlineManager = new OfflineDataManager(this.networkManager);
  
    // await this.offlineManager.init(getContext(this));
  
    this.offlineManager.setOnSyncStart(() => {
      this.syncStatus = '同步中...';
      this.addLog('开始同步数据');
    });
  
    this.offlineManager.setOnSyncComplete((count) => {
      this.syncStatus = '';
      this.addLog(`同步完成: ${count}`);
      this.pendingCount = this.offlineManager?.getPendingCount() || 0;
    });
  
    this.offlineManager.setOnSyncError((error) => {
      this.syncStatus = '';
      this.addLog(`同步错误: ${error.message}`);
    });
  
    this.pendingCount = this.offlineManager.getPendingCount();
  }
  
  aboutToDisappear() {
    this.networkManager.stop();
  }
  
  // 保存测试数据
  async saveTestData() {
    let id = await this.offlineManager?.saveData('test', {
      value: Math.random(),
      timestamp: Date.now()
    });
  
    this.pendingCount = this.offlineManager?.getPendingCount() || 0;
    this.addLog(`保存数据: ${id}`);
  }
  
  // 手动同步
  async manualSync() {
    await this.offlineManager?.syncPendingData();
  }
  
  private addLog(message: string) {
    let time = new Date().toLocaleTimeString();
    this.logs.unshift(`[${time}] ${message}`);
    if (this.logs.length > 20) {
      this.logs.pop();
    }
  }
  
  build() {
    Column() {
      // 状态显示
      Row() {
        Text(`待同步: ${this.pendingCount}`)
          .fontSize(16)
      
        if (this.syncStatus) {
          Text(this.syncStatus)
            .fontSize(14)
            .fontColor('#F5A623')
            .margin({ left: 20 })
        }
      }
      .width('100%')
      .padding(15)
    
      // 操作按钮
      Row() {
        Button('保存测试数据')
          .onClick(() => this.saveTestData())
          .layoutWeight(1)
      
        Button('手动同步')
          .onClick(() => this.manualSync())
          .layoutWeight(1)
          .margin({ left: 10 })
      }
      .width('100%')
      .padding(10)
    
      // 日志
      List() {
        ForEach(this.logs, (log: string, index: number) => {
          ListItem() {
            Text(log)
              .fontSize(12)
              .fontFamily('monospace')
          }
        })
      }
      .width('100%')
      .layoutWeight(1)
      .backgroundColor('#F5F5F5')
      .padding(10)
    }
    .width('100%')
    .height('100%')
  }
}

四、踩坑与注意事项

坑点一:监听未取消

页面销毁时忘记取消监听,导致内存泄漏。

// ❌ 错误:忘记取消监听
aboutToDisappear() {
  // 没有取消监听!
}

// ✅ 正确:取消所有监听
aboutToDisappear() {
  if (this.netConnection) {
    this.netConnection.off('netAvailable');
    this.netConnection.off('netLost');
    this.netConnection.off('netChange');
    this.netConnection = null;
  }
}

坑点二:状态判断时机错误

在监听回调中直接判断状态,可能不准确。

// ❌ 错误:在回调中直接判断
this.netConnection.on('netAvailable', () => {
  // 此时网络可能已经又断了
  this.fetchData();  // 可能失败
});

// ✅ 正确:在回调中重新获取状态
this.netConnection.on('netAvailable', async () => {
  let status = await this.getCurrentNetworkStatus();
  if (status.isConnected) {
    this.fetchData();
  }
});

坑点三:频繁触发处理

网络状态可能频繁变化,需要防抖处理。

// 带防抖的状态处理
class DebouncedNetworkHandler {
  private debounceTimer: number = -1;
  private debounceDelay: number = 1000;
  
  handleNetworkChange(callback: () => void): void {
    // 清除之前的定时器
    if (this.debounceTimer !== -1) {
      clearTimeout(this.debounceTimer);
    }
  
    // 设置新的定时器
    this.debounceTimer = setTimeout(() => {
      callback();
      this.debounceTimer = -1;
    }, this.debounceDelay);
  }
}

五、HarmonyOS 6适配指南

5.1 增强的网络能力检测

HarmonyOS 6提供了更详细的网络能力信息。

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

// HarmonyOS 6: 获取详细网络能力
let netHandle = await connection.getDefaultNet();
let capabilities = await connection.getNetCapabilities(netHandle);

// 网络能力检查
let hasInternet = capabilities.networkCap.includes(connection.NetCap.NET_CAPABILITY_INTERNET);
let hasMms = capabilities.networkCap.includes(connection.NetCap.NET_CAPABILITY_MMS);
let hasInternal = capabilities.networkCap.includes(connection.NetCap.NET_CAPABILITY_INTERNAL_DEFAULT);

// 链路下行带宽
let downlinkKbps = capabilities.linkDownBandwidthKbps;

// 链路上行带宽
let uplinkKbps = capabilities.linkUpBandwidthKbps;

5.2 网络质量评估

HarmonyOS 6支持网络质量评估API。

// HarmonyOS 6: 网络质量评估
let qualityInfo = await connection.getNetworkQuality();

console.info('网络质量评分:', qualityInfo.score);
console.info('时延:', qualityInfo.delay);
console.info('抖动:', qualityInfo.jitter);
console.info('丢包率:', qualityInfo.packetLoss);

六、总结一下下

网络状态监听是现代应用的必备能力。本文从三个场景展开:

基础监听:监听网络连接、断开、切换等事件,获取网络类型、计费状态、信号强度等信息。这是所有网络智能功能的基础。

智能请求策略:根据网络状态自动调整请求参数,如图片尺寸、超时时间、重试策略等。WiFi下无限制,移动网络下适度限制,离线时优雅降级。

离线缓存同步:离线时将数据保存到本地,网络恢复后自动同步。这是提升用户体验的关键,让应用在网络不佳时也能正常使用。

三个常见坑点:监听未取消、状态判断时机错误、频繁触发处理。遇到网络状态问题时,先排查这三个方面。

HarmonyOS 6带来了更详细的网络能力检测和网络质量评估API,让应用能更精准地感知网络环境,做出最优决策。

下一篇文章,我们将深入网络质量评估,实现带宽与延迟检测!

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐