# 应用前后台切换后出现通信不可达、网络异常

#### 问题现象

用户在应用内使用网络进行通信时（或者数据传输和文件上传下载），若将应用切换至后台（例如锁屏或进行其他任务），一段时间后切回前台，可能会遇到网络连接中断、无法发送消息或文件下载失败等问题。

另外，在使用SSH、Mosh或SFTP等终端工具执行远程服务器上的持续任务时，将应用退至后台后，系统会挂起应用进程并释放网络连接，导致会话意外中断，远端任务无法持续跟踪或状态丢失。

#### 背景知识

*   在HarmonyOS中，应用切换到后台2秒后，应用的网络资源会被冻结，并且在12秒后进行释放。此时，再继续使用网络资源，就会出现网络不可用的情况。
*   Background Tasks Kit（后台任务开发服务）：为减少耗电并确保前台体验流畅，当应用退至后台时，系统会对其实施包括进程挂起与终止在内的管控措施。
    1.  短时任务(ArkTS)：应用退至后台一小段时间后，应用进程会被挂起，无法执行对应的任务。如果应用在后台仍需要执行耗时不长的任务，如状态保存等，可以通过本文申请短时任务，扩展应用在后台的运行时间。
    2.  长时任务(ArkTS)：应用退至后台后，在后台需要长时间运行用户可感知的任务，如播放音乐、导航等。为防止应用进程被挂起，导致对应功能异常，可以申请长时任务，使应用在后台长时间运行。
*   由于Socket通信是基于IP和端口进行通信的，在应用退到后台后，网络资源被冻结时会Abort Socket，但是不会释放连接对象。在应用切换到前台后，继续使用之前的连接对象，由于底层已释放资源导致通信失败。

补充说明，startBackgroundRunning的TASK\_KEEPING类型需要系统级ACL特权，普通三方应用无法申请；DATA\_TRANSFER类型在无实际大流量数据吞吐时，会被系统后台一致性校验判定违规并强杀。短时任务有严格的时间限制（最高3分钟）与每日总配额限制。

#### 问题定位

1.  以申请长时任务为例，查看module.json5配置文件中是否有ohos.permission.KEEP\_BACKGROUND\_RUNNING的权限声明，并搜索startBackgroundRunning和stopBackgroundRunning，检查申请和取消长时任务的逻辑是否完善。
2.  监听on('error')事件，判断连接链路是否异常。在连接异常情况下，发送消息报错2301107 Socket not connected。
3.  对于SSH、Mosh等终端工具类应用，检查是否尝试通过backgroundTaskManager申请长时任务保活。由于TASK\_KEEPING需要系统级ACL特权，DATA\_TRANSFER在低频心跳场景下会被一致性校验拦截，普通三方应用无法通过长时任务机制实现终端会话的长时间后台保活。

#### 分析结论

由于应用在使用TCPSocket、UDPSocket等通信时（或者使用相应网络服务进行数据传输或文件上传下载），当应用切换退到后台后，未申请短时任务或长时任务会导致网络资源被冻结和释放。同时，将应用切后台和切回前台，未正确管理对应的通信状态，继续使用之前的TCPSocket、UDPSocket等连接对象和服务器通信，导致出现网络异常。

补充说明，对于SSH、Mosh等终端工具类应用，系统基于功耗与资源调度考量，判定纯粹的终端会话或闲置TCP不属于合法的高优先级后台服务，从而强制回收资源。backgroundTaskManager的长时/短时任务机制无法支撑普通三方应用的长时间网络空载或低频心跳保活需求。

#### 修改建议

*   如果应用有后台使用网络资源的场景，可以在使用该场景时申请短时任务或长时任务，确保网络资源不被冻结和释放。
*   参考应用前后台切换后重连实现在切回前台后重连。注意重连前需要调用close接口主动断开连接，否则connect连接会报错2301103 Connection aborted：
    1.  结合UIAbility组件生命周期的onForeground和onBackground，在前后台切换时，将应用前后台的状态存储在AppStorage中。
    2.  使用@StorageProp将AppStorage存储的前后台状态与对应的属性建立单向数据同步。并使用@Watch监听状态变量的变化，当前后台状态变化后，关闭或重新连接。
*   对于SSH、Mosh等终端工具类应用，建议采用"服务端会话托管+客户端前台断线秒连"的架构设计，放弃在客户端侧申请后台保活：
    1.  服务端调整：执行持续任务时，将任务放在持久化的虚拟终端会话（如tmux session）中，而非直接在基础shell内运行。
    2.  客户端生命周期适配：在UIAbility的onForeground生命周期回调中，加入极速重连逻辑，检查是否有因切后台而中断的会话，触发静默重连。
    3.  状态同步：客户端重连后，下发对应的终端附着命令（如tmux attach -t agent\_session），拉取退后台期间的终端输出日志，实现无缝恢复现场。

```
// 基于UIAbility生命周期的前台重连逻辑
import { UIAbility } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

export default class EntryAbility extends UIAbility {
  onForeground(): void {
    // 检查是否有由于切后台而中断的SSH会话
    if (SessionManager.hasDisconnectedSession()) {
      // 触发静默重连
      SessionManager.reconnectAllActiveSessions().then(() => {
        // 重连成功后，发送自动附着命令恢复现场
        SessionManager.executeCommand('tmux attach -t agent_session_1');
      }).catch((err: BusinessError) => {
        hilog.error(0x0000, 'EntryAbility', `Foreground reconnect failed: ${err.message}`);
        // 通过状态变量通知UI组件展示网络异常提示，引导用户手动重连或稍后重试
        AppStorage.setOrCreate('isNetworkError', true);
      });
    }
  }

  onBackground(): void {
    // 主动释放非必要资源，坦然接受即将到来的TCP冻结
  }
}
```

另外，还有以下备选方案：

*   方案A：使用基于UDP的Mosh协议，Mosh协议本身具有状态漫游特性，退后台中断UDP后，切回前台只要向服务端再发包即可立刻恢复连接，无需手动写重连与附着逻辑，但服务端需预装mosh-server。
*   方案B：仅针对SFTP大文件真实传输场景，按规申请dataTransfer长时任务，系统支持，能保证大文件传完，但传输完毕必须立即释放，不能用于SSH心跳保活，否则将被系统拉黑。

#### 常见FAQ

Q：UDPsocket在手机熄屏或应用切到后台后一段时间，重新切回前台时无法收到消息的原因分析？

A：应用退后台后UDPsocket被abort了，回到前台接收线程会退出并且触发error回调。因此需要监听事件管理，在监听到error事件后重新bind，避免无法收到消息。

Q：SSH、Mosh等终端工具类应用能否通过长时任务实现后台保活？

A：普通三方应用无法通过backgroundTaskManager的长时任务实现终端会话的长时间后台保活。TASK\_KEEPING类型需要系统级ACL特权，DATA\_TRANSFER类型在低频心跳场景下会被一致性校验拦截。建议采用服务端会话托管（如tmux）配合客户端前台重连的架构方案。

Q：应用使用第三方通信库在后台时收不到指令或消息，如何处理？

A：应用退至后台后，网络资源会被冻结和释放，导致基于网络的通信库无法正常收发消息。若应用在后台需要长时间运行用户可感知的通信任务，可以申请长时任务，使应用在后台长时间运行，确保网络资源不被冻结和释放。