按需特性点了暂停,界面还在“安装中”:HarmonyOS 7 下载任务状态怎么防假进度

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

API26 按需特性下载暂停恢复与迟到回调过滤

证据边界:文中 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
downloadingpauseRequestedpausing禁止重复点击,保留当前百分比是
pausingpauseConfirmedpaused显示“已暂停”是
pausingprogress(old generation)pausing只记日志,不刷新进度是
pausedresumeRequestedresuming显示恢复中是
resumingprogress(current generation)downloading恢复更新进度是
任意活动态terminalErrorfailed给出重试或清理入口按错误类型决定

可直接复用的下载状态归并器

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、状态回调、用户意图和恢复入口保持一致。本文用一套任务状态机避免假进度与重复下载。

API26 按需特性下载暂停恢复与迟到回调过滤

证据边界:文中 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 不丢、迟到回调不越权、错误码不混淆,进度条才不会演戏。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐