HarmonyOS 鸿蒙Next中WebSocket连接在应用进入后台后会被系统杀死吗?
HarmonyOS 鸿蒙Next中WebSocket连接在应用进入后台后会被系统杀死吗?
我们的 IM 应用在锁屏 2 分钟后收不到消息。除了申请 continuousTask,还有别的办法吗?
申请长时任务:
应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,使应用在后台长时间运行。在长时任务中,支持同时申请多种类型的任务,也可以对任务类型进行更新。应用退至后台执行业务时,系统会做一致性校验,确保应用在执行相应的长时任务。应用在申请长时任务成功后,通知栏会显示与长时任务相关联的消息,用户删除通知栏消息时,系统会自动停止长时任务。
使用场景:
下表给出了当前长时任务支持的类型,包含数据传输、音视频播放、录制、定位导航、蓝牙相关业务、多设备互联、音视频通话和计算任务。可以参考下表中的场景举例,选择合适的长时任务类型。
表1 长时任务类型
| 参数名 | 描述 | 配置项 | 场景举例 |
|---|---|---|---|
| DATA_TRANSFER | 数据传输。 | dataTransfer | 非托管形式的上传、下载,如在浏览器后台上传或下载数据。 |
| AUDIO_PLAYBACK | 音视频播放。 | audioPlayback | 音频、视频在后台播放,音视频投播。 说明: 支持在元服务中使用。 |
| AUDIO_RECORDING | 录制。 | audioRecording | 录音、录屏退后台。 |
| LOCATION | 定位导航。 | location | 定位、导航。 |
| BLUETOOTH_INTERACTION | 蓝牙相关业务。 | bluetoothInteraction | 通过蓝牙传输文件时退后台。 |
| MULTI_DEVICE_CONNECTION | 多设备互联。 | multiDeviceConnection | 分布式业务连接、投播。 说明: 支持在元服务中使用。 |
| VOIP | 音视频通话。 | voip | 某些聊天类应用(具有音视频业务)音频、视频通话时退后台。 |
| TASK_KEEPING | 计算任务。 说明: 从API version 21开始,对PC/2in1设备、非PC/2in1设备但申请了ACL权限为ohos.permission.KEEP_BACKGROUND_RUNNING_SYSTEM的应用开放。 API version 20及之前版本,仅对PC/2in1设备开放。 |
taskKeeping | 如杀毒软件。 |
详细开发文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/continuous-task
更多关于HarmonyOS 鸿蒙Next中WebSocket连接在应用进入后台后会被系统杀死吗?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
涉及到app保活问题,如果满足条件就申请长时任务。
不然,可以申请短时任务,在短时任务结束后,主动断开WebSocket连接。IM消息走push远程推送。
以下参考:
【背景知识】
- 应用进入后台会触发系统后台功耗管控策略,创建的所有网络连接在较短时间内都会被杀死。
- 网络资源合理使用:无长时任务的应用退到后台主动断开socket连接,包含TCP和UDP连接。
- 应用退至后台后,在后台需要长时间运行用户可感知的任务,如播放音乐、导航等。为防止应用进程被挂起,导致对应功能异常,可以申请长时任务,让应用在后台长时间运行。当前长时任务支持的类型,包含数据传输、音视频播放、录制、定位导航、蓝牙相关业务、多设备互联、WLAN相关业务和计算任务。
【解决方案】
websocket保活,可以使用长时任务保活websocket,应用退到后台会被杀掉进程,长时任务可以防止后台挂起的程序被杀掉。
websocket长连接对应的长时任务类型可以选择voip类型,参考示例如下:
import { webSocket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { wantAgent, WantAgent } from '@kit.AbilityKit';
let defaultIpAddress = "ws://xxxxxx:xxxx";
let ws = webSocket.createWebSocket();
@Component
export struct Socket {
@State message: string = 'ContinuousTask';
// 通过getContext方法,来获取page所在的UIAbility上下文。
private context: Context = getContext(this);
aboutToAppear() {
let wantAgentInfo: wantAgent.WantAgentInfo = {
// 点击通知后,将要执行的动作列表
// 添加需要被拉起应用的bundleName和abilityName
wants: [
{
bundleName: "com.example.myapp",
abilityName: "EntryAbility"
}
],
// 指定点击通知栏消息后的动作是拉起ability
actionType: wantAgent.OperationType.START_ABILITY,
// 使用者自定义的一个私有值
requestCode: 0,
// 点击通知后,动作执行属性
actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
};
try {
// 通过wantAgent模块下getWantAgent方法获取WantAgent对象
wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
try {
let list: Array<string> = ["voip"];
backgroundTaskManager.startBackgroundRunning(this.context, list, wantAgentObj)
.then((res: backgroundTaskManager.ContinuousTaskNotification) => {
console.info("Operation startBackgroundRunning succeeded");
// 此处执行具体的长时任务逻辑,如录音,录制等。
ws.on('open', (err: BusinessError, value: Object) => {
console.log("on open, status:" + value);
// 当收到on('open')事件时,可以通过send()方法与服务器进行通信
ws.send("Hello, server!", (err: BusinessError, value: boolean) => {
if (!err) {
console.log("Message send successfully");
} else {
console.log("Failed to send the message. Err:" + err);
}
});
});
ws.on('message', (err: BusinessError, value: string | ArrayBuffer) => {
console.log("on message, message:" + value);
// 当收到服务器的`bye`消息时(此消息字段仅为示意,具体字段需要与服务器协商),主动断开连接
if (value === 'bye') {
ws.close((err: BusinessError, value: boolean) => {
if (!err) {
console.log("Connection closed successfully");
} else {
console.log("Failed to close the connection. Err:" + err);
}
});
}
});
ws.on('close', (err: BusinessError, value: webSocket.CloseResult) => {
console.log("on close, code is " + value.code + ", reason is " + value.reason);
});
ws.on('error', (err: BusinessError) => {
console.log("on error, error:" + err);
});
ws.connect(defaultIpAddress, (err: BusinessError, value: boolean) => {
if (!err) {
console.log("Connected successfully");
} else {
console.log("Connection failed. Err:" + err);
}
});
})
.catch((error: BusinessError) => {
console.error(`Failed to Operation startBackgroundRunning. code is ${error.code} message is ${error.message}`);
});
} catch (error) {
console.error(`Failed to Operation startBackgroundRunning. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
});
} catch (error) {
console.error(`Failed to Operation getWantAgent. code is ${(error as BusinessError).code} message is ${(error as BusinessError).message}`);
}
}
build() {
Button("发送消息").onClick((event: ClickEvent) => {
ws.send("我发消息了,我是xx")
})
}
}
【总结】 应用切换到后台场景websocket可以申请长时任务来进行保活,申请时应当注意以下几点: 1.应用按需求申请长时任务,当应用无需在后台运行(任务结束)时,要及时主动取消长时任务,否则系统会强行取消。 2.如果进程标记了长时任务,系统会检测标记的任务和实际执行是否符合,不符合也会被杀死。 3.使用数据传输长时任务时,如果进度超过10分钟不更新,任务也会被取消,建议增加更新进度的逻辑,避免未下载完成被系统挂起。
4.建议使用Push Kit的方式去实现,服务器可以定时向客户端发送心跳消息,客户端接收到心跳消息后,可以向服务器发送响应消息,以表明自己仍然处于连接状态。这样可以避免websocket连接因为长时间没有数据传输而被关闭。
在HarmonyOS Next中,WebSocket连接在应用进入后台后可能会被系统挂起或终止,具体取决于系统资源管理策略。系统为优化资源,可能中断后台应用的网络连接。若需维持连接,可申请后台持续任务权限,但需符合系统规范。
在HarmonyOS Next中,应用进入后台(包括锁屏)后,系统为优化资源与功耗,会限制其网络活动,WebSocket连接确实可能被挂起或中断,导致无法实时接收消息。
除了申请continuousTask(持续任务)来保持后台网络连接,你还可以考虑以下技术方案:
-
使用系统推送服务:这是首推的替代方案。HarmonyOS提供了Push Kit。你的服务端将消息发送给华为推送服务器,由系统级服务负责唤醒或通知你的应用。这比维持一个常驻的后台连接更省电、更可靠,是IM类应用的推荐架构。
-
合理使用短时任务:对于需要在后台执行短暂网络同步的任务,可以使用
transientTask。它允许应用在后台获得短时间(约3分钟)的资源访问权限,适合进行数据拉取或连接保活,但无法解决长期实时监听的需求。 -
优化连接与重连策略:
- 在应用从前台切到后台时,主动将WebSocket状态通知服务端,并尝试进入低功耗模式。
- 实现健壮的重连机制。当应用从后台返回前台时,立即检查WebSocket连接状态并进行重建。
- 可以利用后台代理能力,在特定时间间隔进行轻量级的心跳或拉取,但这需要系统调度配合,实时性无法保证。
核心建议:对于用户感知强的即时消息,应优先采用 “系统推送(Push Kit) + 前台WebSocket连接” 的混合模式。应用在前台时使用WebSocket保证实时性和低延迟;应用在后台时,依赖Push Kit送达通知,用户点击通知或打开应用后再通过WebSocket获取完整数据和状态同步。这符合HarmonyOS Next的后台管理设计哲学,能在体验与功耗间取得最佳平衡。

