鸿蒙学习实战之路 - 网络重连最佳实践
网络重连是确保应用在网络波动情况下保持稳定运行的关键技术,合理实现可以显著提升用户体验
关于本文
本文基于华为官方文档整理,结合实际开发经验,提供 HarmonyOS 应用网络重连的实用指南
- 本文并不能代替官方文档,所有内容基于官方文档+实践记录
- 所有代码示例都有详细注释,建议自己动手尝试
- 基本所有关键功能都会附上对应的文档链接,强烈建议你点看看看
- 本文将通过实际案例介绍 HarmonyOS 网络重连的实现方式和最佳实践
代码测试环境
确保你的开发环境符合以下要求:
| 软件/工具 | 版本要求 |
|---|---|
| HarmonyOS SDK | API Level 11+ |
| TypeScript | 5.0+ |
| DevEco Studio | 4.1+ |
| 设备要求 | 支持 HarmonyOS NEXT 的真机或模拟器 |
概述
网络重连是指在网络连接出现中断或异常断开的情况下,设备或应用程序重新建立网络连接的过程。对于许多依赖网络的业务和应用来说,网络重连能够确保在网络出现短暂中断后,业务能够快速恢复,减少因网络故障导致的业务中断时间,提高业务的连续性和可靠性。
在 HarmonyOS 中,网络重连主要应用于以下场景:
- 网络超时重连:客户端向服务器发送请求后,如果发生网络超时,自动尝试重新建立连接
- 网络切换重连:当网络连接发生变化(如从 Wi-Fi 切换到移动数据)后,应用需要检测网络状态变化并重新建立连接
- 应用前后台切换后重连:应用切换到后台一段时间后,网络资源会被冻结释放,需要客户端重新建立连接
- 游戏场景重连:游戏过程中因网络异常导致掉线,支持玩家重新连接回原游戏房间/队伍
本文将从以下几个方面介绍 HarmonyOS 网络重连的最佳实践:
- 网络状态监测与监听
- 不同场景下的网络重连实现
- 网络超时与重试机制
- 游戏场景下的掉线重连
- 网络重连的性能与安全考虑
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 应用前后台切换后重连
场景描述
当应用切换到后台一段时间后,系统可能会冻结或释放应用的网络资源,当应用再次回到前台时,需要重新建立网络连接。
实现原理
通过监听应用的前后台切换事件(onForeground 和 onBackground),当应用从后台切换到前台时,触发网络重连逻辑。
代码实现
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 性能优化
- 控制重试频率
避免过于频繁的重试,建议使用指数退避算法(Exponential Backoff)来逐渐增加重试间隔,减少服务器负载和网络拥塞。
typescript
// 指数退避算法示例
const retryDelay = baseDelay * Math.pow(2, retryAttempts - 1) + Math.random() * jitter;
- 限制重试次数
设置合理的最大重试次数,避免无限重试导致资源浪费。
- 批量重连
当有多个网络连接需要重连时,可以考虑批量处理,避免同时发起过多的网络请求。
- 优先级处理
根据业务重要性设置重连优先级,确保关键业务先重连。
4.2 安全考虑
- 认证信息保护
在重连过程中,避免明文传输敏感信息如认证令牌、密码等。
- 防止重放攻击
为每次重连请求生成唯一标识符,防止攻击者重放重连请求。
- 服务器验证
重连时,服务器应验证客户端身份和会话有效性,防止未授权访问。
- 超时保护
为重连操作设置超时时间,避免无限等待导致应用卡死。
5. 网络重连最佳实践总结
5.1 设计原则
- 透明性:网络重连过程应尽量对用户透明,减少用户感知
- 可靠性:确保重连逻辑能够处理各种网络异常情况
- 灵活性:根据不同业务场景和网络条件调整重连策略
- 可测试性:重连逻辑应易于测试和调试
5.2 常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| 重连失败导致应用卡死 | 设置重连超时和最大重试次数,避免无限等待 |
| 网络频繁波动导致频繁重连 | 增加重连间隔,使用指数退避算法 |
| 重连过程中用户体验差 | 显示重连状态提示,提供取消重连选项 |
| 重连后数据不一致 | 实现数据同步机制,确保重连后数据一致性 |
5.3 监控与分析
- 网络状态监控
实时监控应用的网络状态,收集网络连接、断开、切换等事件数据。
- 重连效果分析
统计重连成功率、平均重连时间、重连次数等指标,评估重连机制的效果。
- 异常情况分析
分析重连失败的原因,如网络类型、地理位置、时间等因素,优化重连策略。
结语
网络重连是 HarmonyOS 应用开发中的重要技术,可以显著提升应用在网络不稳定情况下的用户体验。本文介绍了几种常见的网络重连场景及其实现方式,包括网络超时重连、网络切换重连、应用前后台切换后重连以及游戏场景下的掉线重连。
在实际应用开发中,应根据具体业务需求和场景选择合适的重连策略,并注意性能优化和安全考虑。希望本文的内容能够对你有所帮助,祝你在鸿蒙开发之路上越走越远!
参考文档: