按需特性点了暂停,界面还在“安装中”:HarmonyOS 7 下载任务状态怎么防假进度
按需特性点了暂停,界面还在“安装中”:HarmonyOS 7 下载任务状态怎么防假进度
用户下载一个增强特性,点击暂停后进度条仍继续走;重进页面又从 0% 开始,后台其实保留着原 taskId。HarmonyOS 26.0.0 的按需分发能力新增暂停下载任务接口,真正难点不在调用一次 pause,而在应用如何把 taskId、状态回调、用户意图和恢复入口保持一致。本文用一套任务状态机避免假进度与重复下载。

证据边界:文中 API 26/HarmonyOS 7 能力以华为当前官方文档为准;代码中的应用侧状态与一致性断言已在本机 Node.js 环境执行。当前本机 SDK 为 API 24,且没有连接 HarmonyOS 7 真机,因此本文不声称完成 API 26 编译、真机性能或上架审核验证。
先把故障钉在可复现步骤上
启动一个按需模块下载,在 37% 时点击暂停,同时制造一个迟到的 progress=42 回调;杀进程后恢复页面。日志记录 taskId、generation、intent、platformStatus、displayProgress 与 lastPersistedAt。
案例一:暂停后的迟到进度不能让 UI 回到下载中
pause 成功后提升 generation,旧 generation 的进度只做诊断,不再更新展示状态。UI 显示“已暂停 37%”,恢复动作继续使用原 taskId。
案例二:无效 taskId 要重新建立任务而不是无限恢复
官方 C API 会返回无效 taskId 等错误。遇到明确无效后,清理本地映射并向用户说明需要重新下载;通信异常则保留任务,允许稍后查询,二者不能混为一类。
pause 成功只改变平台任务,不自动修正所有页面
官方接口 HMS_ModuleInstall_PauseTask(taskId) 从 26.0.0 开始提供。应用仍需让当前页面、通知、任务中心和持久化记录看到同一个意图状态。否则平台已经暂停,某个旧页面仍会根据迟到回调显示安装中。
taskId 是可恢复身份,不是页面临时变量
taskId 应与模块、用户和任务代际绑定,并持久化必要状态。进程重启先查询或等待平台回调,再决定显示暂停、下载或完成;不能因为页面重建就创建第二个任务。
错误码决定恢复策略
无效 taskId 表示本地身份已经不能继续使用;内部通信异常则不证明任务不存在。应用要分别处理,不在网络抖动时清掉有效任务,也不在明确无效后无限重试。
可直接放进工程的接口片段
// C API,API 26.0.0+
// ModuleInstall_ErrCode result = HMS_ModuleInstall_PauseTask(taskId);
// E_NO_ERROR: 暂停请求成功
// E_INVALID_TASK_ID: taskId 无效
// E_INTERNAL_COMMUNICATION: 内部通信异常
type DownloadUiState = 'idle'|'downloading'|'paused'|'completed'|'failed'
上面是围绕官方接口位置整理的 ArkTS/工程片段,仍需在 API 26 工具链和目标设备上编译验证。为避免把接口示意当成证据,应用侧关键判定另写成可执行断言:
class DownloadState {
generation=0; state='downloading'; progress=0;
pause(){ this.generation++; this.state='paused'; }
onProgress(g,p){ if(g!==this.generation || this.state==='paused') return; this.progress=p; }
}
const d=new DownloadState(); const old=d.generation; d.progress=37; d.pause(); d.onProgress(old,42);
if(d.state!=='paused'||d.progress!==37) throw new Error('暂停后被迟到进度覆盖');
方案不是越复杂越好
| 方案 | 适用场景 | 放弃它的理由 |
|---|---|---|
| 页面销毁就新建任务 | 不可恢复临时资源 | 重复下载与空间浪费 |
| 暂停后忽略所有平台状态 | 短时离线 | 无法知道完成或任务失效 |
| taskId + generation 状态机 | 正式按需特性 | 需要持久化与回调过滤 |
使用 taskId 保持平台身份,用 generation 管应用展示代际。pause、resume、cancel 和完成都通过同一个仓库广播,页面只观察状态。
最终交付物
一个 DownloadStateStore、错误码分流表和进程恢复步骤。读者可以把它迁移到通知、任务中心与详情页共用。
- 暂停后旧进度回调不改变 UI。
- 恢复继续使用原 taskId。
- 无效 taskId 与通信异常分开处理。
- 多个页面显示同一任务状态。
- 完成后清理持久化映射和临时文件。
暂停接口只是一个动作,可信下载体验需要一条完整生命周期。taskId 不丢、迟到回调不越权、错误码不混淆,进度条才不会演戏。
暂停不是一个按钮状态,而是一条状态转换
下载 UI 最容易犯的错,是把按钮文案直接当成任务事实。用户点了暂停,只能说明“应用已经提出暂停意图”;只有平台回调确认后,任务才进入 paused。迟到的 progress 回调也不能把 paused 页面重新推回 downloading。
| 当前状态 | 事件 | 下一状态 | UI 动作 | 是否保留 taskId |
|---|---|---|---|---|
| downloading | pauseRequested | pausing | 禁止重复点击,保留当前百分比 | 是 |
| pausing | pauseConfirmed | paused | 显示“已暂停” | 是 |
| pausing | progress(old generation) | pausing | 只记日志,不刷新进度 | 是 |
| paused | resumeRequested | resuming | 显示恢复中 | 是 |
| resuming | progress(current generation) | downloading | 恢复更新进度 | 是 |
| 任意活动态 | terminalError | failed | 给出重试或清理入口 | 按错误类型决定 |
可直接复用的下载状态归并器
type DownloadState = 'idle' | 'downloading' | 'pausing' | 'paused' | 'resuming' | 'completed' | 'failed'
type TaskView = { taskId: string; generation: number; state: DownloadState; progress: number }
type TaskEvent =
| { type: 'pauseRequested' }
| { type: 'pauseConfirmed'; generation: number }
| { type: 'resumeRequested' }
| { type: 'progress'; generation: number; value: number }
| { type: 'completed'; generation: number }
| { type: 'failed'; generation: number }
function reduceTask(view: TaskView, event: TaskEvent): TaskView {
if ('generation' in event && event.generation !== view.generation) return view
switch (event.type) {
case 'pauseRequested': return view.state === 'downloading' ? { ...view, state: 'pausing' } : view
case 'pauseConfirmed': return view.state === 'pausing' ? { ...view, state: 'paused' } : view
case 'resumeRequested': return view.state === 'paused' ? { ...view, state: 'resuming', generation: view.generation + 1 } : view
case 'progress':
if (view.state !== 'downloading' && view.state !== 'resuming') return view
return { ...view, state: 'downloading', progress: Math.max(view.progress, event.value) }
case 'completed': return { ...view, state: 'completed', progress: 100 }
case 'failed': return { ...view, state: 'failed' }
}
}
generation 是防止迟到回调污染新任务的关键;taskId 则决定恢复时是否继续原任务。二者不能互相替代。上面的归并器可以做普通 TypeScript 单元测试,但平台 pause/resume 接口仍需在 API 26 工具链和目标设备上验证。
四组回归测试不能省
- 在 37% 点击暂停,再注入 42% 的旧代际进度,页面仍显示“已暂停 37%”。
- 暂停后杀进程并恢复,taskId 与已持久化进度仍能对应。
- 连续点击暂停两次,只向平台发出一次有效请求。
- 恢复后收到旧任务 completed,不得把当前新代际直接置为完成。
这四组测试共同覆盖用户意图、平台确认、进程恢复和迟到事件。只测一次正常暂停,不足以证明不会出现假进度。
官方资料
按需特性点了暂停,界面还在“安装中”:HarmonyOS 7 下载任务状态怎么防假进度
用户下载一个增强特性,点击暂停后进度条仍继续走;重进页面又从 0% 开始,后台其实保留着原 taskId。HarmonyOS 26.0.0 的按需分发能力新增暂停下载任务接口,真正难点不在调用一次 pause,而在应用如何把 taskId、状态回调、用户意图和恢复入口保持一致。本文用一套任务状态机避免假进度与重复下载。

证据边界:文中 API 26/HarmonyOS 7 能力以华为当前官方文档为准;代码中的应用侧状态与一致性断言已在本机 Node.js 环境执行。当前本机 SDK 为 API 24,且没有连接 HarmonyOS 7 真机,因此本文不声称完成 API 26 编译、真机性能或上架审核验证。
先把故障钉在可复现步骤上
启动一个按需模块下载,在 37% 时点击暂停,同时制造一个迟到的 progress=42 回调;杀进程后恢复页面。日志记录 taskId、generation、intent、platformStatus、displayProgress 与 lastPersistedAt。
案例一:暂停后的迟到进度不能让 UI 回到下载中
pause 成功后提升 generation,旧 generation 的进度只做诊断,不再更新展示状态。UI 显示“已暂停 37%”,恢复动作继续使用原 taskId。
案例二:无效 taskId 要重新建立任务而不是无限恢复
官方 C API 会返回无效 taskId 等错误。遇到明确无效后,清理本地映射并向用户说明需要重新下载;通信异常则保留任务,允许稍后查询,二者不能混为一类。
pause 成功只改变平台任务,不自动修正所有页面
官方接口 HMS_ModuleInstall_PauseTask(taskId) 从 26.0.0 开始提供。应用仍需让当前页面、通知、任务中心和持久化记录看到同一个意图状态。否则平台已经暂停,某个旧页面仍会根据迟到回调显示安装中。
taskId 是可恢复身份,不是页面临时变量
taskId 应与模块、用户和任务代际绑定,并持久化必要状态。进程重启先查询或等待平台回调,再决定显示暂停、下载或完成;不能因为页面重建就创建第二个任务。
错误码决定恢复策略
无效 taskId 表示本地身份已经不能继续使用;内部通信异常则不证明任务不存在。应用要分别处理,不在网络抖动时清掉有效任务,也不在明确无效后无限重试。
可直接放进工程的接口片段
// C API,API 26.0.0+
// ModuleInstall_ErrCode result = HMS_ModuleInstall_PauseTask(taskId);
// E_NO_ERROR: 暂停请求成功
// E_INVALID_TASK_ID: taskId 无效
// E_INTERNAL_COMMUNICATION: 内部通信异常
type DownloadUiState = 'idle'|'downloading'|'paused'|'completed'|'failed'
上面是围绕官方接口位置整理的 ArkTS/工程片段,仍需在 API 26 工具链和目标设备上编译验证。为避免把接口示意当成证据,应用侧关键判定另写成可执行断言:
class DownloadState {
generation=0; state='downloading'; progress=0;
pause(){ this.generation++; this.state='paused'; }
onProgress(g,p){ if(g!==this.generation || this.state==='paused') return; this.progress=p; }
}
const d=new DownloadState(); const old=d.generation; d.progress=37; d.pause(); d.onProgress(old,42);
if(d.state!=='paused'||d.progress!==37) throw new Error('暂停后被迟到进度覆盖');
方案不是越复杂越好
| 方案 | 适用场景 | 放弃它的理由 |
|---|---|---|
| 页面销毁就新建任务 | 不可恢复临时资源 | 重复下载与空间浪费 |
| 暂停后忽略所有平台状态 | 短时离线 | 无法知道完成或任务失效 |
| taskId + generation 状态机 | 正式按需特性 | 需要持久化与回调过滤 |
使用 taskId 保持平台身份,用 generation 管应用展示代际。pause、resume、cancel 和完成都通过同一个仓库广播,页面只观察状态。
最终交付物
一个 DownloadStateStore、错误码分流表和进程恢复步骤。读者可以把它迁移到通知、任务中心与详情页共用。
- 暂停后旧进度回调不改变 UI。
- 恢复继续使用原 taskId。
- 无效 taskId 与通信异常分开处理。
- 多个页面显示同一任务状态。
- 完成后清理持久化映射和临时文件。
暂停接口只是一个动作,可信下载体验需要一条完整生命周期。taskId 不丢、迟到回调不越权、错误码不混淆,进度条才不会演戏。
官方资料
更多推荐
所有评论(0)