云计算百科
云计算领域专业知识百科平台

HarmonyOS 7 网络任务与后台传输实战 02:弱网和网络切换下如何保证任务不丢

弱网与网络切换封面

上一篇把后台下载跑起来了,Wi-Fi 环境下一切正常。结果一放到真实手机上测,问题就全来了——用户在地铁里下载,信号忽强忽弱;出门的时候 Wi-Fi 断了切到 4G;网络抖一下任务就失败了,用户还得手动点重试。

正常网络下基本看不出问题,一切到弱网就暴露出来了。这篇就讲讲真实网络环境下,下载任务怎么扛住断网、切网、失败重试这些乱七八糟的情况。说白了,这里要解决的就是两件事:网络不好的时候任务别丢,网络恢复了任务能自动接着跑。

一、用户下载到一半断网了,任务去哪了

最开始我以为 RequestAgent 会自动处理断网重连。Wi-Fi 环境下确实是这样,偶尔断个几百毫秒,系统自己就恢复了,用户根本感觉不到。

真到了弱网环境就不是那么回事了。地铁里信号不稳,TCP 连接直接被掐断,任务状态变成 FAILED。这时候用户怎么办?总不能让用户自己点重试吧?

还有个更隐蔽的问题:应用退到后台,网络切换了(Wi-Fi 切移动数据),等用户切回来,任务状态是 FAILED,但我们 UI 上还显示"下载中"。状态不一致,用户以为在下,其实早就停了。

这里有个坑:网络断开的时候,系统不一定会马上把任务标记为失败。有时候任务卡在 RUNNING 状态,但其实已经下不动了。这种"假活"状态最麻烦——你以为它在跑,其实早就死了。

二、Wi-Fi 切移动网络,任务还能接着下吗

这个问题我最开始也没当回事。网络切换而已,TCP 连接断了重连不就行了?

真测了才发现问题:Wi-Fi 切移动网络的时候,IP 变了,原来的 TCP 连接肯定断。但 RequestAgent 会不会自动用新网络重新连接?

答案是:不一定。取决于任务配置。

如果你创建任务的时候 network 参数只配了 NETWORK_WIFI,那切到移动网络的时候,任务直接暂停,不会自动用移动数据下。这个设计其实挺合理的——用户没开流量的话,偷偷用流量下大文件,用户得投诉死。

但如果你希望用户切到移动网络也能继续下,那 network 参数就要把 NETWORK_MOBILE 也加上。同时还要在 UI 上提示用户"正在使用移动数据下载",让用户知情。

这里有个设计判断:到底要不要允许移动数据下载?我们的做法是:默认只在 Wi-Fi 下下,用户可以手动开"允许移动数据下载"的开关。用户主动开了才切网络继续下,不然切网就暂停。这个度要把握好——既不能偷偷耗用户流量,也不能切个网任务就死了。

失败重试流程图

三、失败了怎么办:自动重试,别让用户手动点

上一篇提了 retry 配置,失败自动重试 3 次。但这里有个问题:重试是系统自动做的,我们怎么知道重试了几次?什么时候算彻底失败了?

刚开始我也想直接靠系统重试就行,不用自己管。实际跑起来发现,系统重试是"闷头试",试完了不管成功失败,也不告诉你试了几次。用户看到的就是进度卡在那,不知道是在重试还是已经死了。

所以我们在应用层也加了一层失败处理。任务状态变成 FAILED 的时候,先别急着提示用户,等几秒看看会不会自动恢复。如果连续几次查询都是 FAILED,再弹提示。

代码放在 entry/src/main/ets/download/DownloadRecovery.ets,负责任务状态恢复和重试。

import { request } from '@kit.BasicServicesKit';

export class DownloadRecovery {
private static retryMap: Map<string, number> = new Map();

// 监听任务状态变化,处理失败重试
static async onTaskStateChanged(
taskId: string,
state: request.DownloadState
): Promise<void> {

switch (state) {
case request.DownloadState.DOWNLOAD_STATE_RUNNING:
// 下载中,重置重试计数
this.retryMap.set(taskId, 0);
break;

case request.DownloadState.DOWNLOAD_STATE_FAILED:
// 失败了,检查要不要自动重试
await this.tryAutoRetry(taskId);
break;

case request.DownloadState.DOWNLOAD_STATE_COMPLETED:
// 下完了,清理重试计数
this.retryMap.delete(taskId);
break;
}
}

// 自动重试逻辑
private static async tryAutoRetry(
taskId: string
): Promise<void> {

const retryCount = this.retryMap.get(taskId) || 0;

// 最多自动重试 3 次
if (retryCount >= 3) {
console.warn('Max retries reached for task: ' + taskId);
// 提示用户手动重试
this.notifyUser(taskId, '下载失败,请手动重试');
return;
}

// 等 3 秒再重试,给网络恢复时间
setTimeout(async () => {
try {
const task = await this.getTask(taskId);
if (task) {
await task.start();
this.retryMap.set(taskId, retryCount + 1);
console.info('Auto retry attempt: ' + (retryCount + 1));
}
} catch (e) {
console.error('Retry failed: ' + e.message);
}
}, 3000);
}

// 重复任务防护:检查是否已有相同任务
static async checkDuplicateTask(
url: string,
savePath: string
): Promise<boolean> {

// 查询所有任务,看看有没有相同 URL 和路径的
const tasks = await request.getRequestAgent('download_agent')
.then(agent => agent.getTaskList());

for (const task of tasks) {
const info = await task.getTaskInfo();
if (info.url === url && info.filePath === savePath) {
// 已经在下载了,不要重复创建
console.info('Duplicate task found: ' + task.taskId);
return true;
}
}

return false;
}
}

这段代码的核心:onTaskStateChanged 监听状态变化,FAILED 的时候自动重试,最多 3 次。checkDuplicateTask 防止用户重复点下载,创建两份一样的任务。

真正运行的时候要注意:重试不要太频繁。网络刚断,你马上重试肯定还是失败。等个 3 秒 5 秒,给网络恢复的时间。另外,重试次数要存下来,不能应用一重启就清零了,不然无限重试。

网络切换与任务恢复示意图

四、App 被切到后台再回来,任务还认不认识

这个问题最开始我没当回事。RequestAgent 的任务不是系统级的吗?应用退了任务还在,那回来的时候直接查任务状态不就行了?

真测了才发现,没那么简单。

应用切到后台,过了十几分钟再回来。我们 UI 上还显示着上次的进度——比如 60%。但实际任务可能早就失败了,或者用户在系统设置里把任务删了。这时候 UI 显示的进度和真实状态完全对不上。

正确的做法是:应用回到前台的时候,主动去查一遍所有任务的真实状态。别相信 UI 上缓存的那个进度数字,那玩意可能早就过期了。

还有个问题:应用被杀了再启动,之前的任务 ID 映射表在内存里,重启之后就没了。所以映射表要存到本地数据库,应用重启之后能读回来,不然你根本不知道哪个任务对应哪个业务。

五、上线前还要处理的几个边界情况

正常功能跑通了,上线前还要过一遍这些边界:

1. 用户主动暂停和网络断开的暂停,要区分开。用户手动暂停的,网络恢复了不要自动开始。网络断开导致的暂停,网络好了可以自动恢复。这两种暂停不能混为一谈。

2. 重复任务防护。用户快速点两次下载按钮,别创建两份一模一样的任务。创建之前先查一下,有没有相同 URL 相同路径的任务在跑。

3. 任务幂等性。同样的下载请求,重复发几次,结果应该是一样的。不能第一次下到 50%,第二次又从头开始。保存路径固定,任务可恢复,就是幂等的基础。

4. 失败任务清理。重试了 3 次还是失败,这个任务就别留着了。要么删掉,要么标记成失败状态,别让它一直在任务列表里占着。

5. 网络状态监听。注册网络状态变化的监听,网络恢复了主动去查一下失败任务,看能不能自动恢复。别光等用户手动点。

说白了,弱网下载这件事,核心不是写下载代码,是写状态管理。什么状态下该做什么操作、失败了怎么恢复、重复了怎么防、切网了怎么处理。这些想清楚了,下载功能在真实环境里才稳。

这两篇下来,从后台下载基础,到弱网和网络切换的异常处理,整个下载链路就算走完了。RequestAgent 这个东西,用起来确实比自己写 http 下载省心,但省心不代表不用管——系统帮你做了底层的活,上层的状态管理和异常兜底,还是得自己写。

赞(0)
未经允许不得转载:网硕互联帮助中心 » HarmonyOS 7 网络任务与后台传输实战 02:弱网和网络切换下如何保证任务不丢
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!