鸿蒙6.0应用开发——网络重连开发
鸿蒙6.0应用开发——网络重连开发
概述
网络重连是指在网络连接出现中断或异常断开的情况下,设备或应用程序重新建立网络连接的过程。对于许多依赖网络的业务和应用来说,网络重连能够确保在网络出现短暂中断后,业务能够快速恢复,减少因网络故障导致的业务中断时间,提高业务的连续性和可靠性。例如,在线金融交易、远程医疗、音视频播放等对实时性和连续性要求较高的业务,网络重连功能至关重要。根据应用的实际场景,网络重连可以分为以下多种方式。
- 网络超时重连:客户端向服务器发送请求后,如果发生网络超时,那么客户端将自动尝试与服务器重新建立连接。
- 网络切换重连:当网络连接发生变化后,客户端应用可能会出现网络异常。应用需要检测网络状态变化,并根据网络状态进行连接。
- 应用前后台切换后重连:应用切换到后台一段时间后,网络资源会被冻结释放,需要客户端重新建立连接。
网络超时重连
场景描述
在网络请求中,经常会遇到网络波动、服务器宕机等情况,从而导致网络不可用、网络超时等问题。为了减少网络超时等带来的影响,在实际应用开发中经常使用超时机制和重试机制。如HTTP请求列表数据时,设置HTTP连接超时和请求重试。
- 网络超时机制是指在网络通信过程中,当一个操作在规定的时间内没有得到预期的响应或完成时,系统会自动判定该操作失败,并触发相应的处理逻辑。其原理是通过设置一个定时器,从网络操作开始时计时,一旦超过设定的时间阈值,就认为操作超时。
- 重试机制一般配合超时机制一起使用,指的是多次发送相同的请求来避免瞬态故障和偶然性故障。
实现原理
网络超时分为网络连接超时和网络读取超时。
- 网络连接超时就是在程序默认的等待时间内没有得到服务器的响应。
- 网络读取超时指客户端在读取服务器响应时等待的时间。
网络重试常用的策略有定时重试、指数退避重试、随机退避重试等。
- 定时重试:设定一个固定的重试次数,当网络请求失败时,在该次数范围内进行重试,每次重试之间的时间间隔可以是固定的,也可以根据具体情况进行调整。例如,设定重试次数为 3 次,每次重试间隔为 2 秒。
- 指数退避重试:每次重试的时间间隔按照指数级增长,如重试间隔时间依次为2、4、8、16等。
- 随机退避重试:每次重试的时间间隔在一个指定的范围内随机取值。例如,设定重试间隔时间在 1 秒到 5 秒之间随机,这样可以避免多个请求同时重试,分散服务器的负载压力,提高整体的重试成功率。
在设置重试策略时,需要根据实际的场景来进行设置,既要考虑网络超时的时间,还需要关注重试的次数和时间间隔,避免网络资源浪费。
开发步骤
在HarmonyOS中,在Http、RCP发生错误或者超时后,都可以使用网络超时重连的机制。HTTP超时重连的实现步骤如下所示。
- 设置HTTP请求的读取超时时间、连接超时时间。
- 可以根据网络状态进行判断,然后再进行重连。这样可以在非网络问题的情况下进行重试,可以更精准地控制重试行为,提高请求的成功率和效率。例如,对于一些表示服务错误的响应码(如 500 Internal Server Error、503 Service Unavailable 等),可以进行重试。
- 使用setTimeout进行函数执行延迟,配合使用Promise,进而同步获取网络请求结果。
HTTP超时重连的代码如下所示:
async getHttpRequest(url: string, retry: number): Promise<number | undefined> {
try {
return await this.httpRequest?.requestInStream(url,
{ method: http.RequestMethod.GET, connectTimeout: 6000, readTimeout: 60000 })
.then((data: number) => {
if ((data === 408 || data === 500) && retry > 0) {
return new Promise((resolve: Function) => {
setTimeout(() => {
resolve(this.getHttpRequest(url, retry - 1));
}, 2000);
});
} else {
return data;
}
});
} catch (err) {
this.isDownload = false;
try {
this.getUIContext().getPromptAction().showToast({ message: $r('app.string.download_error') });
} catch (error) {
let err = error as BusinessError;
hilog.error(0xFF00, 'NetworkReconnection', `showToast fail, code = ${err.code}, message = ${err.message}`);
}
return;
}
}
代码逻辑走读:
- 方法定义与参数说明:定义了一个名为
getHttpRequest的异步方法,接受两个参数:url(请求的URL字符串)和retry(重试次数)。 - HTTP请求:使用
httpRequest对象的requestInStream方法发起GET请求,设置连接和读取超时为6000和60000毫秒。 - 数据处理:请求成功后,检查返回的HTTP状态码。如果状态码为408或500,并且还有剩余重试次数,则进入重试逻辑。
- 重试机制:在重试逻辑中,使用
setTimeout延迟2秒后递归调用getHttpRequest方法,递减重试次数。 - 正常响应处理:如果没有重试或请求成功,直接返回响应数据。
- 异常处理:在请求过程中捕获异常,设置
isDownload为false,并尝试显示错误提示。如果显示失败,记录错误日志。
RCP超时重连的实现与HTTP的实现步骤类似,RCP超时重连的代码如下所示:
createRCPSession(): rcp.Session | null {
try {
const customHttpEventsHandler: rcp.HttpEventsHandler = {
onDownloadProgress: (totalSize: number, transferredSize: number) => {
this.contentLength = totalSize;
this.downloadSize = transferredSize;
this.process = this.contentLength === 0 ? 0 : Math.floor(this.downloadSize / this.contentLength * 100);
},
onDataEnd: () => {
this.contentLength = -1;
this.downloadSize = 0;
},
};
const sessionConfig: rcp.SessionConfiguration = {
requestConfiguration: {
transfer: {
timeout: {
connectMs: 6000,
transferMs: 60000
}
},
tracing: { httpEventsHandler: customHttpEventsHandler }
}
}
return rcp.createSession(sessionConfig);
} catch (error) {
return null;
}
}
// ...
async getRcpRequest(url: string, retry: number): Promise<rcp.Response | undefined> {
try {
if (this.session !== null) {
return await this.session.get(url)
.then((response) => {
if ((response.statusCode === 408 || response.statusCode === 500) && retry > 0) {
return new Promise((resolve: Function) => {
setTimeout(() => {
resolve(this.getRcpRequest(url, retry - 1));
}, 2000)
})
} else {
return response;
}
})
} else {
return;
}
} catch (err) {
try {
this.getUIContext().getPromptAction().showToast({ message: $r('app.string.download_error') });
this.isDownload = false;
} catch (error) {
let err = error as BusinessError;
Logger.error('NetworkReconnection', `showToast fail, code = ${err.code}, message = ${err.message}`);
}
return;
}
}
代码逻辑走读:
- 定义了一个
createRCPSession方法,用于创建一个RCP会话。- 在尝试块中,首先定义了一个自定义的HTTP事件处理器
customHttpEventsHandler,用于处理下载进度和数据结束事件。 - 然后定义了一个会话配置
sessionConfig,其中包括请求配置和超时设置。 - 最后调用
rcp.createSession方法创建会话并返回。 - 如果在创建过程中发生异常,则返回
null。
- 在尝试块中,首先定义了一个自定义的HTTP事件处理器
- 定义了一个异步方法
getRcpRequest,用于发送HTTP GET请求。- 在尝试块中,首先检查会话是否存在。
- 如果会话存在,则发送GET请求,并处理可能的超时重试逻辑。
- 如果请求失败(状态码为408或500),则进行重试,每次重试间隔2秒。
- 如果请求成功,则直接返回响应。
- 如果会话不存在,则返回
undefined。 - 在捕获异常块中,显示错误提示信息,并记录错误日志。
- 在代码的最后部分,定义了一些辅助方法和属性,用于管理会话和请求的状态。
网络切换重连
场景描述
在当下这个数字化时代,大部分的应用确实离不开网络,网络已经深度渗透到各类应用场景之中。然而,在网络状态切换后,如何继续保持网络连接是许多应用需要处理的问题。
实现原理
网络切换主要分为网络类型切换和无网络与有网络之间的切换。针对网络切换的场景,HarmonyOS提供了网络连接管理能力,用于查询网络信息、监听网络连接的变化等。
在实现网络切换重连上,主要包含以下步骤。
- 使用网络连接管理的能力监听网络变化,并使用AppStorage存储应用全局网络状态。
- 使用@StorageProp,将AppStorage存储的网络状态与对应的属性建立单向数据同步。
- 使用@Watch监听状态变量的变化,当状态变量变化后,对网络进行关闭或重连。
开发步骤
调用connection的register()方法订阅网络变化通知,同时,订阅netCapabilitiesChange网络能力变化事件,订阅netLost网络丢失事件。在设备从有网络到无网络状态会触发netLost事件,从无网络到有网络会触发netCapabilitiesChange事件。而在网络类型切换时,也会触发netLost事件和netCapabilitiesChange事件,开发者可以根据实际场景需要在netCapabilitiesChange事件中,将网络类型及网络状态存储在AppStorage中。
private netCon: connection.NetConnection = connection.createNetConnection();
register() {
this.netCon.register((error: BusinessError) => {
Logger.error('net register' + JSON.stringify(error));
});
}
netCapabilitiesChange() {
this.netCon.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
let netAvailable = false;
data.netCap.networkCap?.forEach((value) => {
if (value === connection.NetCap.NET_CAPABILITY_INTERNET) {
netAvailable = true;
}
})
Logger.info('ConnectionUtil.netAvailable:' + netAvailable);
AppStorage.setOrCreate('netAvailable', netAvailable);
})
this.netCon.on('netLost', (data: connection.NetHandle) => {
AppStorage.setOrCreate('netAvailable', false);
Logger.info("WifiChangeListen-- Succeeded to get data: " + JSON.stringify(data));
});
}
代码逻辑走读:
- 创建网络连接对象
netCon,用于后续的网络操作。 - 定义
register方法,调用netCon.register进行网络注册,并通过Logger.error记录注册过程中的错误信息。 - 定义
netCapabilitiesChange方法,首先监听netCapabilitiesChange事件,当网络能力发生变化时,检查网络是否具备互联网能力(NET_CAPABILITY_INTERNET),并将结果记录到日志和应用存储中。 - 同样地,监听
netLost事件,当网络丢失时,将网络可用状态设置为false,并记录日志信息。
使用AppStorage存储应用全局网络状态。
@StorageProp('netAvailable') @Watch('onSocketUpdated') netAvailable: boolean = true;
使用@Watch监听状态变量的变化,并根据对应的变化和实际应用场景重新连接网络。
onSocketUpdated() {
this.netAvailable ? this.tcpSocketConnect() : this.tcpSocketDisconnect();
Logger.info('netAvailable:' + this.netAvailable);
}
// ...
tcpSocketConnect() {
if (!this.netAvailable) {
try {
this.getUIContext().getPromptAction().showToast({ message: $r('app.string.connect_error') });
} catch (error) {
let err = error as BusinessError;
Logger.error('NetworkReconnection', `showToast fail, code = ${err.code}, message = ${err.message}`);
}
return;
}
this.tcp = socket.constructTCPSocketInstance();
this.tcp.on('connect', () => {
Logger.info('on connect');
});
this.tcp.on('close', () => {
Logger.info('on close');
});
this.tcp.on('error', (error: BusinessError) => {
Logger.error('error:' + error.code + error.message
);
});
const clientIpAddress: socket.NetAddress = {
address: CommonConstants.IP_ADDRESS,
port: CommonConstants.CLIENT_IP_PORT
} as socket.NetAddress;
this.tcp.bind(clientIpAddress, (err: BusinessError) => {
if (err) {
Logger.error('bind fail' + JSON.stringify(err));
this.getUIContext().getPromptAction().showToast({ message: $r('app.string.connect_error') });
return;
}
let tcpConnect: socket.TCPConnectOptions = {} as socket.TCPConnectOptions;
const serverIpAddress: socket.NetAddress = {
address: CommonConstants.IP_ADDRESS,
port: CommonConstants.IP_PORT
} as socket.NetAddress;
tcpConnect.address = serverIpAddress;
tcpConnect.timeout = 3000;
this.tcp?.connect(tcpConnect, (err: BusinessError) => {
if (err) {
Logger.error('connect fail');
this.getUIContext().getPromptAction().showToast({ message: $r('app.string.connect_error') });
return;
}
});
});
}
代码逻辑走读:
- 网络状态更新处理:
- 函数
onSocketUpdated()检查网络状态netAvailable,如果网络可用,调用tcpSocketConnect()进行连接;否则调用tcpSocketDisconnect()断开连接,并记录网络状态日志。
- 函数
- TCP套接字连接逻辑:
- 函数
tcpSocketConnect()首先检查网络状态,如果网络不可用,显示连接错误提示并记录错误日志;否则继续执行连接操作。 - 创建TCP套接字实例,并设置连接、关闭、错误事件的回调函数,用于记录相关日志。
- 定义客户端IP地址,并绑定到TCP套接字。如果绑定失败,记录错误日志并显示连接错误提示。
- 定义服务器IP地址和连接选项,并尝试连接到服务器。如果连接失败,记录错误日志并显示连接错误提示。
- 函数
- 错误处理:
- 在绑定和连接操作中,如果发生错误,记录错误日志并显示连接错误提示。
- 日志记录:
- 使用
Logger.info和Logger.error记录网络状态和错误信息。
- 使用
- 提示显示:
- 使用
showToast方法显示网络连接错误提示。
- 使用
无网络切换有网络后重连下载效果图如下。
图1 无网络切换有网络后重连下载

应用前后台切换后重连
场景描述
应用在使用TCPSocket、UDPSocket等通信时,如果未申请长时任务或短时任务,当应用退到后台一段时间后,可能遇到网络不可用或网络资源异常的情况,并且将应用切回前台后,继续使用之前的TCPSocket、UDPSocket等连接对象继续和服务器通信也可能出现网络异常。
实现原理
在HarmonyOS中,应用切换到后台2秒后,应用的网络资源会被冻结,并且在12秒后进行释放。此时,再继续使用网络资源,就会出现网络不可用的情况。如果应用有后台使用网络资源的场景,可以使用短时任务或长时任务。
由于Socket通信是基于IP和端口进行通信的,在应用退到后台后,网络资源被冻结时会清空TCP、UDP连接对象的IP和端口,但是不会释放连接对象。在应用切换到前台后,系统会给连接对象重新分配新的IP和端口,继续使用之前的连接对象与服务器进行通信时,服务器会认为同一个连接对象前后IP和端口不一致,从而导致通信不可信、网络异常。
应用前后台切换后网络重连在实现上有以下两个关键部分。
- 结合UIAbility组件生命周期onForeground和onBackground,在前后台切换时,将应用前后台的状态存储在AppStorage中。
- 使用@StorageProp将AppStorage存储的前后台状态与对应的属性建立单向数据同步。并使用@Watch监听状态变量的变化,当前后台变化后,关闭或重新连接。
开发步骤
前后台切换时,在UIAbility组件生命周期中存储前后台状态。
export default class EntryAbility extends UIAbility {
// ...
onForeground(): void {
// Ability has brought to foreground
// ...
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
// Ability has back to background
// ...
AppStorage.setOrCreate('onForeground', false);
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}
}
使用@StorageProp同步前后台状态,并使用@Watch监听状态变化。
@StorageProp('onForeground') @Watch('onForegroundChange') onForeground: boolean = true;
// ...
onForegroundChange(): void {
this.onForeground ? this.tcpSocketConnect() : this.tcpSocketDisconnect();
}
前后台切换网络重连实现效果如下:
图2 应用前后台切换后重连下载
更多推荐



所有评论(0)