鸿蒙学习实战之路 - 网络重连最佳实践

鸿蒙学习实战之路 - 网络重连最佳实践

网络重连是确保应用在网络波动情况下保持稳定运行的关键技术,合理实现可以显著提升用户体验

关于本文

本文基于华为官方文档整理,结合实际开发经验,提供 HarmonyOS 应用网络重连的实用指南

华为开发者联盟 - 应用网络重连

  • 本文并不能代替官方文档,所有内容基于官方文档+实践记录
  • 所有代码示例都有详细注释,建议自己动手尝试
  • 基本所有关键功能都会附上对应的文档链接,强烈建议你点看看看
  • 本文将通过实际案例介绍 HarmonyOS 网络重连的实现方式和最佳实践

代码测试环境

确保你的开发环境符合以下要求:

软件/工具 版本要求
HarmonyOS SDK API Level 11+
TypeScript 5.0+
DevEco Studio 4.1+
设备要求 支持 HarmonyOS NEXT 的真机或模拟器

概述

网络重连是指在网络连接出现中断或异常断开的情况下,设备或应用程序重新建立网络连接的过程。对于许多依赖网络的业务和应用来说,网络重连能够确保在网络出现短暂中断后,业务能够快速恢复,减少因网络故障导致的业务中断时间,提高业务的连续性和可靠性。

在 HarmonyOS 中,网络重连主要应用于以下场景:

  1. 网络超时重连:客户端向服务器发送请求后,如果发生网络超时,自动尝试重新建立连接
  2. 网络切换重连:当网络连接发生变化(如从 Wi-Fi 切换到移动数据)后,应用需要检测网络状态变化并重新建立连接
  3. 应用前后台切换后重连:应用切换到后台一段时间后,网络资源会被冻结释放,需要客户端重新建立连接
  4. 游戏场景重连:游戏过程中因网络异常导致掉线,支持玩家重新连接回原游戏房间/队伍

本文将从以下几个方面介绍 HarmonyOS 网络重连的最佳实践:

  1. 网络状态监测与监听
  2. 不同场景下的网络重连实现
  3. 网络超时与重试机制
  4. 游戏场景下的掉线重连
  5. 网络重连的性能与安全考虑

1. 网络状态监测与监听

1.1 网络状态监听基础

在 HarmonyOS 中,可以使用 connection 模块来监听网络状态变化,这是实现网络重连的基础。

typescript 复制代码
import connection from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

export class NetworkMonitor {
  private netConnection: connection.NetConnection;

  constructor() {
    // 创建网络连接实例
    this.netConnection = connection.createNetConnection();
  }

  // 注册网络状态监听
  registerNetworkListener() {
    this.netConnection.register((error: BusinessError) => {
      if (error) {
        console.error(`网络监听注册失败: ${JSON.stringify(error)}`);
        return;
      }

      // 监听网络可用事件
      this.netConnection.on('netAvailable', (data) => {
        console.log(`网络已可用: ${JSON.stringify(data)}`);
        // 网络恢复后执行重连逻辑
        this.handleNetworkReconnect();
      });

      // 监听网络能力变化事件
      this.netConnection.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
        console.log(`网络能力变化: ${JSON.stringify(data)}`);
        // 根据网络能力变化调整重连策略
        this.adjustReconnectStrategy(data);
      });

      // 监听网络丢失事件
      this.netConnection.on('netLost', (data) => {
        console.log(`网络已丢失: ${JSON.stringify(data)}`);
        // 网络丢失时执行相关处理
        this.handleNetworkLost();
      });

      // 监听网络不可用事件
      this.netConnection.on('netUnavailable', (data) => {
        console.log(`网络不可用: ${JSON.stringify(data)}`);
        // 网络不可用时执行相关处理
        this.handleNetworkUnavailable();
      });
    });
  }

  // 处理网络重连
  private handleNetworkReconnect() {
    console.log('执行网络重连逻辑');
    // 此处添加具体的重连逻辑
  }

  // 根据网络能力调整重连策略
  private adjustReconnectStrategy(netCapabilities: connection.NetCapabilityInfo) {
    console.log('根据网络能力调整重连策略');
    // 此处添加根据网络能力调整重连策略的逻辑
  }

  // 处理网络丢失
  private handleNetworkLost() {
    console.log('处理网络丢失情况');
    // 此处添加网络丢失时的处理逻辑
  }

  // 处理网络不可用
  private handleNetworkUnavailable() {
    console.log('处理网络不可用情况');
    // 此处添加网络不可用时的处理逻辑
  }

  // 注销网络状态监听
  unregisterNetworkListener() {
    this.netConnection.unregister();
    console.log('网络状态监听已注销');
  }
}

1.2 获取当前网络状态

在实现网络重连之前,通常需要先获取当前的网络状态,以便决定是否需要执行重连操作。

typescript 复制代码
import connection from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 获取当前网络状态
export async function getCurrentNetworkStatus(): Promise<connection.NetAvailableInfo | null> {
  try {
    const netAvailableInfo = await connection.getNetAvailable();
    return netAvailableInfo;
  } catch (error) {
    console.error(`获取网络状态失败: ${JSON.stringify(error as BusinessError)}`);
    return null;
  }
}

// 检查网络是否可用
export async function isNetworkAvailable(): Promise<boolean> {
  const netStatus = await getCurrentNetworkStatus();
  return netStatus?.isAvailable || false;
}

// 获取当前网络类型
export async function getCurrentNetworkType(): Promise<connection.NetBearType | null> {
  try {
    const netCapabilities = await connection.getNetCapabilities();
    return netCapabilities?.bearerTypes[0] || null;
  } catch (error) {
    console.error(`获取网络类型失败: ${JSON.stringify(error as BusinessError)}`);
    return null;
  }
}

2. 不同场景下的网络重连实现

2.1 网络超时重连

场景描述

在网络请求中,经常会遇到网络波动、服务器宕机等情况,从而导致网络不可用、网络超时等问题。为了减少网络超时等带来的影响,在实际应用开发中经常使用超时机制和重试机制。

实现原理

网络超时分为网络连接超时和网络读取超时:

  • 连接超时:客户端尝试与服务器建立连接但未能在规定时间内完成
  • 读取超时:客户端已经与服务器建立连接,但未能在规定时间内获取到响应数据

重试机制一般配合超时机制一起使用,指的是多次发送相同的请求来避免瞬态故障和偶然性故障。

代码实现

typescript 复制代码
import http from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

// 网络请求配置
interface RequestConfig {
  url: string;
  method?: http.RequestMethod;
  header?: Record<string, string>;
  data?: any;
  timeout?: number;
}

// 重试配置
interface RetryConfig {
  maxRetries: number;
  retryDelay: number;
}

// 带重试机制的网络请求
export async function requestWithRetry(
  config: RequestConfig,
  retryConfig: RetryConfig = { maxRetries: 3, retryDelay: 1000 }
): Promise<http.HttpResponse> {
  let retries = 0;
  const { maxRetries, retryDelay } = retryConfig;

  while (true) {
    try {
      console.log(`发起网络请求 (尝试 ${retries + 1}/${maxRetries + 1}): ${config.url}`);

      // 创建HTTP请求
      const request = http.createHttp();
      const response = await request.request(config.url, {
        method: config.method || http.RequestMethod.GET,
        header: config.header,
        extraData: config.data,
        readTimeout: config.timeout || 30000,
        connectTimeout: config.timeout || 30000
      });

      // 关闭HTTP请求
      request.destroy();

      // 检查响应状态
      if (response.responseCode >= 200 && response.responseCode < 300) {
        console.log(`网络请求成功: ${config.url}`);
        return response;
      } else {
        console.warn(`网络请求返回非成功状态码: ${response.responseCode}`);
        throw new Error(`HTTP Error: ${response.responseCode}`);
      }
    } catch (error) {
      retries++;
      console.error(`网络请求失败 (${retries}/${maxRetries + 1}): ${(error as BusinessError).message}`);

      // 检查是否达到最大重试次数
      if (retries > maxRetries) {
        console.error(`达到最大重试次数 (${maxRetries}), 请求失败: ${config.url}`);
        throw error;
      }

      // 等待重试延迟
      await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, retries - 1)));
      console.log(`等待 ${retryDelay * Math.pow(2, retries - 1)}ms 后重试...`);
    }
  }
}

// 使用示例
async function fetchData() {
  try {
    const response = await requestWithRetry({
      url: 'https://api.example.com/data',
      method: http.RequestMethod.GET,
      timeout: 10000
    }, {
      maxRetries: 5,
      retryDelay: 500
    });

    const data = JSON.parse(response.result as string);
    console.log('获取数据成功:', data);
    return data;
  } catch (error) {
    console.error('获取数据失败:', error);
    // 显示错误提示
  }
}

2.2 网络切换重连

场景描述

当设备的网络连接发生变化时(如从 Wi-Fi 切换到移动数据,或从移动数据切换到 Wi-Fi),应用的网络连接可能会暂时中断,需要重新建立连接。

实现原理

通过监听网络状态变化事件,当检测到网络类型或网络能力发生变化时,自动触发重连逻辑。

代码实现

typescript 复制代码
import connection from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';

class NetworkSwitchHandler {
  private netConnection: connection.NetConnection;
  private previousNetworkType: connection.NetBearType | null = null;
  private reconnectHandlers: Array<() => Promise<void>> = [];

  constructor() {
    this.netConnection = connection.createNetConnection();
    this.setupNetworkListener();
  }

  // 设置网络监听器
  private setupNetworkListener() {
    this.netConnection.register((error: BusinessError) => {
      if (error) {
        console.error(`网络监听注册失败: ${JSON.stringify(error)}`);
        return;
      }

      // 监听网络能力变化
      this.netConnection.on('netCapabilitiesChange', async (data: connection.NetCapabilityInfo) => {
        const currentNetworkType = data.bearerTypes[0];
        console.log(`网络类型变化: ${this.previousNetworkType} -> ${currentNetworkType}`);

        // 检查网络类型是否发生变化
        if (this.previousNetworkType && currentNetworkType !== this.previousNetworkType) {
          console.log('网络切换检测到,执行重连逻辑');
          // 执行所有重连处理函数
          for (const handler of this.reconnectHandlers) {
            try {
              await handler();
            } catch (error) {
              console.error(`重连处理失败: ${JSON.stringify(error)}`);
            }
          }
        }

        // 更新之前的网络类型
        this.previousNetworkType = currentNetworkType;
      });
    });
  }

  // 注册重连处理函数
  registerReconnectHandler(handler: () => Promise<void>) {
    this.reconnectHandlers.push(handler);
  }

  // 取消注册重连处理函数
  unregisterReconnectHandler(handler: () => Promise<void>) {
    this.reconnectHandlers = this.reconnectHandlers.filter(h => h !== handler);
  }

  // 清理资源
  destroy() {
    this.netConnection.unregister();
    this.reconnectHandlers = [];
  }
}

// 使用示例
const networkSwitchHandler = new NetworkSwitchHandler();

// 注册WebSocket重连处理
networkSwitchHandler.registerReconnectHandler(async () => {
  console.log('WebSocket 重连...');
  // 执行WebSocket重连逻辑
});

// 注册数据同步重连处理
networkSwitchHandler.registerReconnectHandler(async () => {
  console.log('数据同步重连...');
  // 执行数据同步重连逻辑
});

2.3 应用前后台切换后重连

场景描述

当应用切换到后台一段时间后,系统可能会冻结或释放应用的网络资源,当应用再次回到前台时,需要重新建立网络连接。

实现原理

通过监听应用的前后台切换事件(onForegroundonBackground),当应用从后台切换到前台时,触发网络重连逻辑。

代码实现

typescript 复制代码
import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

class AppLifecycleHandler {
  private isInBackground: boolean = false;
  private reconnectHandlers: Array<() => Promise<void>> = [];

  // 应用切换到前台
  onForeground() {
    this.isInBackground = false;
    console.log('应用切换到前台');
    // 执行所有重连处理函数
    this.executeReconnectHandlers();
  }

  // 应用切换到后台
  onBackground() {
    this.isInBackground = true;
    console.log('应用切换到后台');
    // 可以在此处执行资源释放逻辑
  }

  // 执行所有重连处理函数
  private async executeReconnectHandlers() {
    for (const handler of this.reconnectHandlers) {
      try {
        await handler();
      } catch (error) {
        console.error(`重连处理失败: ${JSON.stringify(error)}`);
      }
    }
  }

  // 注册重连处理函数
  registerReconnectHandler(handler: () => Promise<void>) {
    this.reconnectHandlers.push(handler);
  }

  // 取消注册重连处理函数
  unregisterReconnectHandler(handler: () => Promise<void>) {
    this.reconnectHandlers = this.reconnectHandlers.filter(h => h !== handler);
  }

  // 检查应用是否在后台
  isAppInBackground(): boolean {
    return this.isInBackground;
  }
}

// 在Ability中使用
export default class EntryAbility extends UIAbility {
  private appLifecycleHandler: AppLifecycleHandler;

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
    hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onCreate');
    this.appLifecycleHandler = new AppLifecycleHandler();
    
    // 注册网络重连处理
    this.appLifecycleHandler.registerReconnectHandler(async () => {
      console.log('执行网络重连');
      // 此处添加网络重连逻辑
    });
  }

  onForeground() {
    hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onForeground');
    this.appLifecycleHandler.onForeground();
  }

  onBackground() {
    hilog.info(0x0000, 'EntryAbility', '%{public}s', 'Ability onBackground');
    this.appLifecycleHandler.onBackground();
  }

  // 其他生命周期方法...
}

3. 游戏场景下的掉线重连

3.1 游戏重连概述

在游戏过程中,因网络状况不佳、操作不当等原因,可能会导致意外掉线的情况。HarmonyOS 提供了游戏联机对战服务,支持玩家在掉线后重新连接回原游戏房间/队伍。

3.2 游戏重连实现

场景一:主动关闭客户端导致掉线

玩家进入房间/队伍后,因主动关闭客户端而导致的掉线,需重新登录游戏并重连联机对战服务器。

typescript 复制代码
import { Client, JoinRoomConfig } from '@kit.GameKit';

class GameReconnectManager {
  private client: Client;

  constructor(client: Client) {
    this.client = client;
  }

  // 初始化游戏客户端
  async initGameClient() {
    try {
      const response = await this.client.Init();
      
      if (response.RtnCode === 0) {
        console.log('游戏客户端初始化成功');
        
        // 检查是否存在未完成的游戏会话
        await this.checkPendingGameSession();
      } else {
        console.error('游戏客户端初始化失败:', response.RtnCode);
      }
    } catch (error) {
      console.error('初始化游戏客户端时发生错误:', error);
    }
  }

  // 检查是否存在未完成的游戏会话
  private async checkPendingGameSession() {
    // 检查是否存在已加入的房间
    const lastRoomId = this.client.GetLastRoomId();
    if (lastRoomId) {
      console.log(`检测到未完成的房间会话: ${lastRoomId}`);
      // 尝试重新加入房间
      await this.rejoinRoom(lastRoomId);
      return;
    }

    // 检查是否存在已加入的队伍
    const lastGroupId = this.client.GetLastGroupId();
    if (lastGroupId) {
      console.log(`检测到未完成的队伍会话: ${lastGroupId}`);
      // 尝试重新加入队伍
      await this.rejoinGroup(lastGroupId);
      return;
    }

    console.log('没有检测到未完成的游戏会话');
  }

  // 重新加入房间
  private async rejoinRoom(roomId: string) {
    try {
      const joinRoomConfig = new JoinRoomConfig();
      joinRoomConfig.RoomId = roomId;
      
      const response = await this.client.JoinRoom(joinRoomConfig);
      
      if (response.RtnCode === 0) {
        console.log(`重新加入房间成功: ${roomId}`);
        // 处理重新加入房间后的逻辑
        this.handleRoomRejoined(response);
      } else {
        console.error(`重新加入房间失败: ${response.RtnCode}`);
        // 处理加入失败的情况
        this.handleRoomRejoinFailed(roomId, response.RtnCode);
      }
    } catch (error) {
      console.error(`重新加入房间时发生错误: ${error}`);
    }
  }

  // 重新加入队伍
  private async rejoinGroup(groupId: string) {
    try {
      // 实现重新加入队伍的逻辑
      console.log(`重新加入队伍: ${groupId}`);
      // ...
    } catch (error) {
      console.error(`重新加入队伍时发生错误: ${error}`);
    }
  }

  // 处理重新加入房间成功
  private handleRoomRejoined(response: any) {
    console.log('处理重新加入房间后的逻辑');
    // 恢复游戏状态
    // 同步游戏数据
    // ...
  }

  // 处理重新加入房间失败
  private handleRoomRejoinFailed(roomId: string, errorCode: number) {
    console.log(`处理重新加入房间失败 (错误码: ${errorCode})`);
    // 显示错误提示
    // 引导用户创建新游戏或加入其他房间
    // ...
  }
}

场景二:网络异常导致掉线

网络异常导致玩家客户端与联机对战服务端连接不上,在一定周期后服务器会将该玩家设置会掉线状态。

typescript 复制代码
import { Room } from '@kit.GameKit';

class NetworkDisconnectHandler {
  private room: Room;
  private reconnectAttempts: number = 0;
  private maxReconnectAttempts: number = 10;
  private reconnectInterval: number = 3000;
  private reconnectTimer: number | null = null;

  constructor(room: Room) {
    this.room = room;
    this.setupDisconnectListener();
  }

  // 设置断线监听器
  private setupDisconnectListener() {
    this.room.onDisconnect((playerInfo) => {
      console.log(`玩家掉线: ${JSON.stringify(playerInfo)}`);
      
      // 检查是否是当前玩家掉线
      if (playerInfo.playerId === this.room.playerId) {
        console.log('当前玩家断线,开始重连');
        this.startReconnectProcess();
      } else {
        console.log('其他玩家掉线,更新玩家状态');
        // 处理其他玩家掉线的逻辑
      }
    });
  }

  // 开始重连过程
  private startReconnectProcess() {
    this.reconnectAttempts = 0;
    this.attemptReconnect();
  }

  // 尝试重连
  private attemptReconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      console.error('达到最大重连尝试次数,重连失败');
      this.handleReconnectFailed();
      return;
    }

    this.reconnectAttempts++;
    console.log(`重连尝试 ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);

    this.room.reconnect()
      .then(() => {
        console.log('重连成功');
        this.handleReconnectSuccess();
      })
      .catch((error) => {
        console.error(`重连失败: ${JSON.stringify(error)}`);
        
        // 检查错误类型
        if (!error.code) {
          // 网络不通,继续重试
          console.log('网络不通,将在3秒后重试');
          this.reconnectTimer = setTimeout(() => {
            this.attemptReconnect();
          }, this.reconnectInterval) as unknown as number;
        } else {
          // 其他错误,如超过允许重连时间
          console.error(`重连错误: ${error.code}`);
          this.handleReconnectFailed();
        }
      });
  }

  // 处理重连成功
  private handleReconnectSuccess() {
    console.log('重连成功,恢复游戏状态');
    // 清除重连定时器
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    // 恢复游戏状态
    // 同步游戏数据
    // ...
  }

  // 处理重连失败
  private handleReconnectFailed() {
    console.log('重连失败,执行失败处理逻辑');
    // 清除重连定时器
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
    // 显示重连失败提示
    // 引导用户返回主界面或重新开始游戏
    // ...
  }

  // 取消重连
  cancelReconnect() {
    console.log('取消重连');
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  // 清理资源
  destroy() {
    this.cancelReconnect();
  }
}

4. 网络重连的性能与安全考虑

4.1 性能优化

  1. 控制重试频率

避免过于频繁的重试,建议使用指数退避算法(Exponential Backoff)来逐渐增加重试间隔,减少服务器负载和网络拥塞。

typescript 复制代码
// 指数退避算法示例
const retryDelay = baseDelay * Math.pow(2, retryAttempts - 1) + Math.random() * jitter;
  1. 限制重试次数

设置合理的最大重试次数,避免无限重试导致资源浪费。

  1. 批量重连

当有多个网络连接需要重连时,可以考虑批量处理,避免同时发起过多的网络请求。

  1. 优先级处理

根据业务重要性设置重连优先级,确保关键业务先重连。

4.2 安全考虑

  1. 认证信息保护

在重连过程中,避免明文传输敏感信息如认证令牌、密码等。

  1. 防止重放攻击

为每次重连请求生成唯一标识符,防止攻击者重放重连请求。

  1. 服务器验证

重连时,服务器应验证客户端身份和会话有效性,防止未授权访问。

  1. 超时保护

为重连操作设置超时时间,避免无限等待导致应用卡死。

5. 网络重连最佳实践总结

5.1 设计原则

  1. 透明性:网络重连过程应尽量对用户透明,减少用户感知
  2. 可靠性:确保重连逻辑能够处理各种网络异常情况
  3. 灵活性:根据不同业务场景和网络条件调整重连策略
  4. 可测试性:重连逻辑应易于测试和调试

5.2 常见问题与解决方案

问题 解决方案
重连失败导致应用卡死 设置重连超时和最大重试次数,避免无限等待
网络频繁波动导致频繁重连 增加重连间隔,使用指数退避算法
重连过程中用户体验差 显示重连状态提示,提供取消重连选项
重连后数据不一致 实现数据同步机制,确保重连后数据一致性

5.3 监控与分析

  1. 网络状态监控

实时监控应用的网络状态,收集网络连接、断开、切换等事件数据。

  1. 重连效果分析

统计重连成功率、平均重连时间、重连次数等指标,评估重连机制的效果。

  1. 异常情况分析

分析重连失败的原因,如网络类型、地理位置、时间等因素,优化重连策略。

结语

网络重连是 HarmonyOS 应用开发中的重要技术,可以显著提升应用在网络不稳定情况下的用户体验。本文介绍了几种常见的网络重连场景及其实现方式,包括网络超时重连、网络切换重连、应用前后台切换后重连以及游戏场景下的掉线重连。

在实际应用开发中,应根据具体业务需求和场景选择合适的重连策略,并注意性能优化和安全考虑。希望本文的内容能够对你有所帮助,祝你在鸿蒙开发之路上越走越远!


参考文档

相关推荐
酒尘&5 小时前
JS数组不止Array!索引集合类全面解析
开发语言·前端·javascript·学习·js
冬夜戏雪5 小时前
【java学习日记】【2025.12.7】【7/60】
java·开发语言·学习
wubba lubba dub dub7506 小时前
第二十八周 学习周报
学习
思成不止于此6 小时前
MySQL 查询实战(三):排序与综合练习
数据库·笔记·学习·mysql
QiZhang | UESTC6 小时前
学习日记day42
学习
深海潜水员6 小时前
OpenGL 学习笔记 第一章:绘制一个窗口
c++·笔记·学习·图形渲染·opengl
义一7 小时前
华为eNSP示例说明网关地址和终端IP地址不在同一网段能正常通信吗
网络
2401_860319527 小时前
在React Native鸿蒙跨平台开发中实现 二叉搜索树,如何实现一些基本的遍历方法,如中序遍历,中序遍历按顺序访问左子树、根节点、右子树
react native·react.js·harmonyos
大、男人8 小时前
DeepAgent学习
人工智能·学习