HarmonyOS 实况窗教程:上传进度实时展示

一、概述

实况窗(Live View)是 HarmonyOS 提供的系统级实时信息展示能力,可将应用的进行中任务以卡片形式展示在实况窗、通知中心和锁屏上。与普通通知不同,实况窗支持实时更新进度点击回跳应用

本教程以「云星图」项目的图片上传场景为例,讲解:

  1. 实况窗的创建与进度更新 – 进度条布局 + 胶囊状态 + 序列号递增
  2. keepAlive 续期机制 – 防止 keepTime 到期导致实况窗消失
  3. 与后台长时任务联动 – 后台上传时不被系统杀进程
  4. WantAgent 点击回跳 – 用户点击实况窗回到应用
  5. 生命周期管理 – 初始化/更新/失败/关闭的完整闭环

整体架构如下:

系统层

StarMapLiveViewController

应用层

EntryAbility.onCreate
init() 缓存 WantAgent

NormalUpLoad.doBatchUpload
上传流程

EntryAbility.onForeground
上传完成则关闭

EntryAbility.onDestroy
关闭实况窗

init
构建 WantAgent

startUpload
创建实况窗 + 启动续期

updateProgress
更新进度/文案/胶囊

showFailed
切换失败样式

keepAlive
每 10s 续期

stop
keepTime=0 关闭

liveViewManager
startLiveView / updateLiveView / stopLiveView

backgroundTaskManager
startBackgroundRunning / stopBackgroundRunning

wantAgent
getWantAgent

二、前置准备

2.1 AGC 开通实况窗权益

必须在 AGC(AppGallery Connect)开通 Live View Kit 权益后才能使用。未开通时调用 startLiveView 会报错(错误码 67108900),且上架后功能会失效。
官方参考文档: 开发准备-Live View Kit(实况窗服务)-应用服务 - 华为HarmonyOS开发者

开通步骤:

  1. 登录 AppGallery Connect 后台
  2. 进入「我的项目」-> 选择目标项目 -> 「HarmonyOS 应用」
  3. 在「API 管理」或「增长」->「实况窗」中找到 Live View Kit
  4. 点击「开通」并提交场景说明文档(描述使用实况窗的业务场景)
  5. 等待审核(约 3 个工作日)

登录 AGC 后台

选择目标项目

找到 Live View Kit

提交场景说明文档

审核通过?

权益开通成功
可调用 startLiveView

修改文档重新提交

开发阶段可申请
设备白名单调试

调试白名单: 开发阶段如果权益尚未正式开通,可以在 AGC 中申请将测试设备加入白名单,白名单设备上可直接调试实况窗功能。

Push Token 获取: 实况窗底层依赖 Push Kit 基础设施,应用启动时需获取 Push Token,否则 startLiveView 可能无法正常工作。本项目已在 EntryAbility.onCreate 中完成:

import { pushService } from '@kit.PushKit';

// EntryAbility.onCreate 中获取 Push Token
pushService.getToken().then((token: string) => {
  hilog.info(DOMAIN, 'testTag', 'Push Token: %{public}s', token);
}).catch((err: BusinessError) => {
  hilog.error(DOMAIN, 'testTag', 'Failed to get push token: %{public}d %{public}s', err.code, err.message);
});

参考文档: 实况窗准备工作

2.2 依赖导入

import { liveViewManager } from '@kit.LiveViewKit';
import { common, Want, WantAgent, wantAgent } from '@kit.AbilityKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { BusinessError } from '@kit.BasicServicesKit';

2.3 module.json5 配置

实况窗在 module.json5 中不需要声明 requestPermissions(AGC 权益是平台级开通,不在 module.json5 中配置,申请通过后需要手动签名)。但如果实况窗展示的是后台任务进度(如本教程的上传场景),需要配置后台长时任务:

{
  "module": {
    "requestPermissions": [
      {
        // 后台长时任务必需权限
        "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"
      }
    ],
    "abilities": [
      {
        "name": "EntryAbility",
        // 声明后台模式为数据传输
        "backgroundModes": ["dataTransfer"]
      }
    ]
  }
}

配置说明:

配置项 作用
KEEP_BACKGROUND_RUNNING 权限 允许应用在后台运行长时任务
backgroundModes: ["dataTransfer"] 声明后台任务类型为数据传输,系统据此决定保活策略

2.4 LiveView 数据结构

实况窗的核心数据结构 LiveView 包含主体区域(primary)和胶囊区域(capsule):

LiveView

id: 实况窗唯一标识

event: 事件类型
'DELIVERY'

sequence: 序列号
每次更新必须递增

isMute: 是否静默

liveViewData

primary(主体区域)

capsule(胶囊区域)

title: 标题

content: 内容文案数组

keepTime: 存活秒数

clickAction: 点击回跳 WantAgent

layoutData: 布局数据
进度条/图标等

type: 胶囊类型

icon: 胶囊图标

content: 胶囊文案

三、封装实况窗控制器

将实况窗的所有操作封装为静态管理器 StarMapLiveViewController,供上传流程调用。

3.1 初始化:缓存 WantAgent

WantAgent 用于实况窗的点击回跳,在应用启动时提前构建并缓存,避免上传时异步等待:

export class StarMapLiveViewController {
  private static liveView: liveViewManager.LiveView | undefined = undefined;
  private static keepAliveTimer: number = -1;
  private static cachedWantAgent: WantAgent | undefined = undefined;
  private static provider: string = '';
  private static uploadDone: boolean = false;

  // 在 EntryAbility.onCreate() 中调用
  static async init(context: common.UIAbilityContext): Promise<void> {
    StarMapLiveViewController.cachedWantAgent = await StarMapLiveViewController.buildWantAgent(context);
    console.info('[LiveView] WantAgent 已缓存');
  }

  private static async buildWantAgent(context: common.UIAbilityContext): Promise<WantAgent> {
    const info: wantAgent.WantAgentInfo = {
      wants: [{
        bundleName: context.abilityInfo.bundleName,
        abilityName: 'EntryAbility'
      } as Want],
      actionType: wantAgent.OperationType.START_ABILITIES,
      requestCode: 0,
      actionFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };
    return await wantAgent.getWantAgent(info);
  }
}

为什么提前缓存? wantAgent.getWantAgent 是异步操作,如果在上传开始时才构建会导致实况窗创建延迟。在 onCreate 中提前构建,上传时直接使用。

EntryAbility.onCreate 中调用:

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
  // ... 其他初始化
  void StarMapLiveViewController.init(this.context);
}

3.2 创建实况窗:startUpload

上传开始时创建实况窗,展示初始进度 0/N:

static async startUpload(total: number, provider: string): Promise<void> {
  StarMapLiveViewController.uploadDone = false;
  StarMapLiveViewController.provider = provider;

  StarMapLiveViewController.liveView = {
    id: 1001,
    event: 'DELIVERY',
    sequence: 1,
    isMute: false,
    liveViewData: {
      primary: {
        title: '云星图上传中',
        content: [{ text: `已完成 0/${total}` }],
        keepTime: 15,                                           // 15 秒后自动消失
        clickAction: StarMapLiveViewController.cachedWantAgent!, // 点击回跳应用
        extensionData: {
          text: provider,                                        // 显示图床厂商名
          type: liveViewManager.ExtensionType.EXTENSION_TYPE_COMMON_TEXT
        },
        layoutData: {
          layoutType: liveViewManager.LayoutType.LAYOUT_TYPE_PROGRESS,
          progress: 0,                                           // 进度 0%
          color: '#FF3880F4',                                    // 进度条颜色(蓝)
          backgroundColor: '#33FFFFFF',                          // 背景色
          nodeIcons: ['start.png', 'ok.png']                     // 起止图标
        }
      },
      capsule: {
        type: liveViewManager.CapsuleType.CAPSULE_TYPE_TEXT,
        status: 1,
        icon: 'logo.png',                                        // 应用图标
        title: '云星图',
        content: `0/${total}`,                                   // 胶囊显示 0/N
        backgroundColor: '#FF3497FF'
      }
    }
  };

  try {
    await liveViewManager.startLiveView(StarMapLiveViewController.liveView);
    StarMapLiveViewController.startKeepAlive();
    console.info(`[LiveView] 开始上传 ${total}`);
  } catch (e) {
    const err = e as BusinessError;
    if (err.code === 1003500006) {
      // 实况窗已存在(上次未被正确关闭),直接更新
      StarMapLiveViewController.liveView.sequence =
        (StarMapLiveViewController.liveView.sequence ?? 1) + 1;
      await liveViewManager.updateLiveView(StarMapLiveViewController.liveView);
      StarMapLiveViewController.startKeepAlive();
      return;
    }
    console.error(`[LiveView] startLiveView error: ${err.code} ${err.message}`);
    StarMapLiveViewController.liveView = undefined;
  }
}

创建流程:

否: code=1003500006

否: 其他错误

startUpload(total, provider)

构建 LiveView 对象
progress=0, keepTime=15

liveViewManager.startLiveView(lv)

成功?

startKeepAlive()
启动 10s 续期定时器

实况窗已存在
递增 sequence + updateLiveView

liveView = undefined
放弃创建

实况窗展示中
进度 0/N

关键设计点:

  • id: 1001:实况窗唯一标识,同一 id 的实况窗全局只有一个。更新和关闭都基于这个 id。
  • event: 'DELIVERY':事件类型,系统据此选择展示模板。常用值有 DELIVERY(配送/进行中场景,适合上传进度展示)、TAXI(出行场景)、FLIGHT(航班场景)等。
  • sequence: 1:序列号,每次 updateLiveView 必须比上次大,否则系统忽略更新。
  • keepTime: 15:实况窗存活 15 秒,到期自动消失。通过 keepAlive 续期机制保持长期显示。
  • 错误码 1003500006:实况窗已存在(上次未正确关闭),此时不能再次 startLiveView,改为 updateLiveView。
  • nodeIcons: ['start.png', 'ok.png']:进度条两端的图标,开始和完成时分别展示。图片需放在 resources/rawfile/ 目录下。

3.3 更新进度:updateProgress

每上传完一张图片调用一次,更新进度条、文案和胶囊:

static async updateProgress(completed: number, total: number): Promise<void> {
  if (StarMapLiveViewController.liveView === undefined) return;

  const done: boolean = completed >= total && total > 0;
  const progress: number = total > 0 ? Math.round((completed / total) * 100) : 0;

  // 1. 递增序列号(必须比上次大)
  StarMapLiveViewController.liveView.sequence =
    (StarMapLiveViewController.liveView.sequence ?? 1) + 1;

  // 2. 更新主体区域
  StarMapLiveViewController.liveView.liveViewData.primary.title =
    done ? '上传完成' : '云星图上传中';
  StarMapLiveViewController.liveView.liveViewData.primary.content =
    [{ text: done ? `${total}` : `已完成 ${completed}/${total}` }];

  // 3. 更新进度条
  const ld = StarMapLiveViewController.liveView.liveViewData.primary.layoutData
    as liveViewManager.ProgressLayout;
  if (ld !== undefined) {
    ld.progress = progress;
  }

  // 4. 更新胶囊
  if (StarMapLiveViewController.liveView.liveViewData.capsule !== undefined) {
    StarMapLiveViewController.liveView.liveViewData.capsule.content =
      done ? '上传完成' : `${completed}/${total}`;
  }

  // 5. 提交更新
  try {
    await liveViewManager.updateLiveView(StarMapLiveViewController.liveView);
  } catch (e) {
    const err = e as BusinessError;
    console.error(`[LiveView] updateLiveView error: ${err.code} ${err.message}`);
  }

  if (done) {
    StarMapLiveViewController.uploadDone = true;
  }
}

3.4 展示失败:showFailed

上传出现失败时,切换为红色失败样式:

static async showFailed(successCount: number, failedCount: number, total: number): Promise<void> {
  if (StarMapLiveViewController.liveView === undefined) return;

  const remaining: number = total - successCount - failedCount;
  StarMapLiveViewController.liveView.sequence =
    (StarMapLiveViewController.liveView.sequence ?? 1) + 1;

  // 切换失败样式
  StarMapLiveViewController.liveView.liveViewData.primary.title = '上传失败';
  StarMapLiveViewController.liveView.liveViewData.primary.content =
    [{ text: `成功 ${successCount} 张,剩余 ${remaining}` }];

  const ld = StarMapLiveViewController.liveView.liveViewData.primary.layoutData
    as liveViewManager.ProgressLayout;
  if (ld !== undefined) {
    ld.progress = 100;
    ld.color = '#FFFF4444';           // 红色进度条
    ld.nodeIcons = ['start.png', 'fail.png'];  // 失败图标
  }

  if (StarMapLiveViewController.liveView.liveViewData.capsule !== undefined) {
    StarMapLiveViewController.liveView.liveViewData.capsule.content = '上传失败';
  }

  try {
    await liveViewManager.updateLiveView(StarMapLiveViewController.liveView);
  } catch (e) {
    const err = e as BusinessError;
    console.error(`[LiveView] showFailed error: ${err.code} ${err.message}`);
  }
  StarMapLiveViewController.uploadDone = true;
}

三种状态的样式对比:

上传失败

title: 上传失败

content: 成功 7 张,剩余 3 张

progress: 100%

color: #FF4444 红色

icons: start.png + fail.png

capsule: 上传失败

上传完成

title: 上传完成

content: 共 10 张

progress: 100%

color: #3880F4 蓝色

icons: start.png + ok.png

capsule: 上传完成

上传中

title: 云星图上传中

content: 已完成 3/10

progress: 30%

color: #3880F4 蓝色

icons: start.png + ok.png

capsule: 3/10

效果展示:

IMG_20260723_010407

3.5 keepAlive 续期机制

实况窗的 keepTime 默认只有 15 秒,到期后自动消失。对于上传这种耗时操作,需要定期续期:

// 每 10 秒续期一次,防止 keepTime 到期自动消失
private static startKeepAlive(): void {
  StarMapLiveViewController.stopKeepAlive();
  StarMapLiveViewController.keepAliveTimer = setInterval(() => {
    if (StarMapLiveViewController.liveView !== undefined) {
      // 续期 = 递增 sequence + 调用 updateLiveView
      StarMapLiveViewController.liveView.sequence =
        (StarMapLiveViewController.liveView.sequence ?? 1) + 1;
      liveViewManager.updateLiveView(StarMapLiveViewController.liveView).catch(() => {});
    }
  }, 10000);
}

private static stopKeepAlive(): void {
  if (StarMapLiveViewController.keepAliveTimer !== -1) {
    clearInterval(StarMapLiveViewController.keepAliveTimer);
    StarMapLiveViewController.keepAliveTimer = -1;
  }
}

为什么是 10 秒? keepTime 设为 15 秒,续期间隔 10 秒,留 5 秒余量,确保在到期前完成续期。

续期机制的工作原理:

系统 liveViewManager keepAlive 定时器 应用 系统 liveViewManager keepAlive 定时器 应用 --- 每 10 秒续期 --- 保持显示 loop [每 10 秒] --- 上传完成 --- startLiveView(keepTime=15s) 实况窗创建,15s 后将消失 updateLiveView(sequence++) 重置 keepTime 计时器 stopKeepAlive() stopLiveView(keepTime=0) 立即关闭实况窗

3.6 关闭实况窗:stop

上传完成或应用退出时关闭实况窗:

static async stop(): Promise<void> {
  StarMapLiveViewController.stopKeepAlive();  // 先停止续期定时器

  if (StarMapLiveViewController.liveView === undefined) return;

  const lv = StarMapLiveViewController.liveView;
  lv.sequence = (lv.sequence ?? 1) + 1;
  lv.liveViewData.primary.title = '上传完成';
  lv.liveViewData.primary.content = [{ text: '所有图片已上传' }];
  lv.liveViewData.primary.keepTime = 0;       // 设为 0 立即关闭
  const ld = lv.liveViewData.primary.layoutData as liveViewManager.ProgressLayout;
  if (ld !== undefined) {
    ld.progress = 100;
  }
  if (lv.liveViewData.capsule !== undefined) {
    lv.liveViewData.capsule.content = '上传完成';
  }

  try {
    await liveViewManager.stopLiveView(lv);
    console.info('[LiveView] 实况窗已关闭');
  } catch (e) {
    const err = e as BusinessError;
    console.error(`[LiveView] stop error: ${err.code} ${err.message}`);
  }
  StarMapLiveViewController.liveView = undefined;
}

关键点: keepTime = 0 是关闭实况窗的信号,系统收到后会立即移除实况窗。

四、与上传流程集成

4.1 上传流程中的调用

NormalUpLoad.doBatchUpload 中,实况窗与上传循环、后台任务紧密配合:

private async doBatchUpload(config: CloudConfig) {
  this.isAllUploading = true;
  const idleItems = this.uploadList.filter(item => item.status === 'idle' || item.status === 'failed');
  const total: number = idleItems.length;
  let completed: number = 0;
  let failedCount: number = 0;
  let hasFailed: boolean = false;
  const providerName: string = getProviderMeta(config.type)?.name ?? config.type;

  // 1. 创建实况窗 + 启动后台长时任务
  await StarMapLiveViewController.startUpload(total, providerName);
  const wantAgent: WantAgent | undefined = StarMapLiveViewController.getWantAgent();
  if (wantAgent !== undefined) {
    backgroundTaskManager.startBackgroundRunning(this.context,
      ['dataTransfer'], wantAgent).catch(() => {});
  }

  // 2. 逐张上传,每完成一张更新实况窗进度
  for (const item of idleItems) {
    this.updateItemStatus(item.id, 'uploading', 0);
    await this.uploadSingleItem(item, config);
    completed++;
    if (item.status === 'failed') {
      failedCount++;
      hasFailed = true;
      break;
    }
    void StarMapLiveViewController.updateProgress(completed, total);
  }

  // 3. 停止后台长时任务
  this.isAllUploading = false;
  void backgroundTaskManager.stopBackgroundRunning(this.context).catch(() => {});

  // 4. 根据结果更新实况窗最终状态
  if (hasFailed) {
    const successCount = completed - failedCount;
    void StarMapLiveViewController.showFailed(successCount, failedCount, total);
  }
  // 全部成功时,实况窗在 onForeground 中由 stop() 关闭
  // ... 省略 UI 状态更新与上传记录刷新等非实况窗相关代码
}

上传流程与实况窗的完整交互时序:

云存储 backgroundTaskManager LiveViewController NormalUpLoad 云存储 backgroundTaskManager LiveViewController NormalUpLoad 后台保活,防止进程被杀 loop [逐张上传] onForeground 时 isUploadDone=true ->> stop() alt [全部成功] [有失败] startUpload(total, providerName) 创建实况窗 progress=0 startKeepAlive() startBackgroundRunning(dataTransfer) uploadObject(item) 上传成功 updateItemStatus(success) updateProgress(completed, total) sequence++ + 更新进度/文案/胶囊 stopBackgroundRunning() 刷新首页图片列表 showFailed(successCount, failedCount, total) 切换红色失败样式

4.2 生命周期管理

实况窗需要在多个生命周期节点进行管理:

// EntryAbility.ets

async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
  // 应用启动时缓存 WantAgent(不启动实况窗)
  void StarMapLiveViewController.init(this.context);
}

onForeground(): void {
  // 应用回到前台时,如果上传已完成则关闭实况窗
  if (StarMapLiveViewController.isUploadDone()) {
    void StarMapLiveViewController.stop();
  }
}

onDestroy(): void {
  // 应用销毁时关闭实况窗
  void StarMapLiveViewController.stop();
}

生命周期管理流程:

回到前台

上传过程

应用启动

全部完成

有失败

应用销毁

onDestroy

stop()
关闭实况窗
停止 keepAlive

onCreate

LiveViewController.init
缓存 WantAgent

应用就绪
(无实况窗)

doBatchUpload

startUpload
创建实况窗 + keepAlive

逐张上传

updateProgress
更新进度

uploadDone = true

showFailed
红色失败样式

onForeground

isUploadDone()?

stop()
关闭实况窗

保持实况窗
继续展示进度

为什么在 onForeground 中关闭? 上传完成时应用可能在后台,用户看到实况窗显示"上传完成"后回到应用。此时实况窗已无意义,在 onForeground 中检测到 uploadDone = true 就关闭它,提供干净的体验。

五、后台长时任务配合

实况窗展示上传进度时,上传任务本身需要在后台持续运行。backgroundTaskManager 提供了长时任务保活能力:

import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { WantAgent } from '@kit.AbilityKit';

// 开始上传时启动后台长时任务
const wantAgent: WantAgent | undefined = StarMapLiveViewController.getWantAgent();
if (wantAgent !== undefined) {
  backgroundTaskManager.startBackgroundRunning(this.context,
    ['dataTransfer'], wantAgent).catch(() => {});
}

// 上传完成后停止
void backgroundTaskManager.stopBackgroundRunning(this.context).catch(() => {});

实况窗与后台任务的协作关系:

上传结束

stopBackgroundRunning
释放后台保活

stop / showFailed
关闭或切换失败样式

stopKeepAlive
停止续期定时器

上传进行中

后台持续上传

updateProgress
更新实况窗

keepAlive 定时器
每 10s 续期实况窗

上传开始

startBackgroundRunning
dataTransfer + WantAgent

系统保活进程
允许后台网络传输

startLiveView
实况窗展示进度

startKeepAlive
10s 续期

两者的分工:

后台长时任务 实况窗
作用 防止应用进程被系统杀死 向用户展示任务进度
核心 API backgroundTaskManager liveViewManager
启动时机 上传开始 上传开始
停止时机 上传结束 上传结束 + 应用回前台
需要权限 KEEP_BACKGROUND_RUNNING 无(需 AGC 权益)
需要配置 backgroundModes

六、完整 API 速查

6.1 StarMapLiveViewController 方法

方法 调用时机 作用
init(context) EntryAbility.onCreate 缓存 WantAgent
startUpload(total, provider) 上传开始 创建实况窗 + 启动续期
updateProgress(completed, total) 每完成一张 更新进度/文案/胶囊
showFailed(success, fail, total) 上传有失败 切换红色失败样式
stop() 上传完成/应用销毁 关闭实况窗 + 停止续期
isUploadDone() onForeground 查询上传是否完成
getWantAgent() 启动后台任务时 获取缓存的 WantAgent

6.2 liveViewManager API

API 作用 注意事项
startLiveView(lv) 创建实况窗 同 id 已存在会报 1003500006
updateLiveView(lv) 更新实况窗 sequence 必须比上次大
stopLiveView(lv) 关闭实况窗 keepTime 设为 0

6.3 LiveView 关键字段

字段 说明 必填
id 实况窗唯一标识
sequence 序列号,每次更新必须递增
liveViewData.primary.title 主体标题
liveViewData.primary.content 主体内容文案数组
liveViewData.primary.keepTime 存活秒数,0 = 立即关闭
liveViewData.primary.clickAction 点击回跳的 WantAgent
liveViewData.primary.layoutData 进度条布局(ProgressLayout)
liveViewData.capsule 胶囊区域

七、常见问题与最佳实践

7.1 常见问题

问题 原因 解决
startLiveView 报 67108900 AGC 未开通 Live View Kit 权益 按 2.1 节步骤在 AGC 开通权益或申请白名单
实况窗更新不生效 sequence 未递增或比上次小 每次更新前 sequence = sequence + 1
实况窗 15 秒后消失 未启动 keepAlive 续期 startUpload 后调用 startKeepAlive
startLiveView 报 1003500006 上次实况窗未正确关闭 catch 中改为 updateLiveView
后台上传被系统杀进程 未启动后台长时任务 配合 backgroundTaskManager.startBackgroundRunning
点击实况窗无反应 未设置 clickAction 或 WantAgent 无效 onCreate 中提前 init 缓存 WantAgent
应用销毁后实况窗残留 未在 onDestroy 中关闭 onDestroy 调用 stop()

7.2 最佳实践

  1. 提前缓存 WantAgentwantAgent.getWantAgent 是异步操作,在 onCreate 中提前构建,避免上传时延迟。

  2. sequence 严格递增:系统通过 sequence 判断是否为有效更新。每次 updateLiveViewstopLiveView 前都必须递增。

  3. keepAlive 间隔小于 keepTimekeepTime = 15s,续期间隔 10s,留 5 秒余量确保不中断。

  4. 错误码 1003500006 容错startLiveView 失败时检查是否为"已存在"错误,改为 updateLiveView 而非直接放弃。

  5. onForeground 检测关闭:用户回到应用时如果上传已完成,及时关闭实况窗,避免残留。

  6. 后台任务配对startBackgroundRunningstopBackgroundRunning 必须严格配对,否则系统会一直保活进程。

  7. 失败样式区分:用红色进度条 + 失败图标让用户一眼看出失败,比纯文字更直观。

八、总结

实况窗是展示进行中任务进度的最佳方式,本教程以图片上传为例,讲解了完整实现:

  • 初始化onCreate 中缓存 WantAgent,避免异步延迟
  • 创建startUpload 构建 LiveView(进度条布局 + 胶囊),处理"已存在"错误码
  • 更新updateProgress 递增 sequence + 更新进度/文案/胶囊
  • 续期keepAlive 每 10 秒 updateLiveView 防止 keepTime 到期消失
  • 失败showFailed 切换红色进度条 + 失败图标
  • 关闭stopkeepTime=0 + stopLiveView + 停止续期定时器
  • 后台配合backgroundTaskManager 长时任务保活,backgroundModes: ["dataTransfer"] 声明后台模式

开发建议:

  1. 实况窗和后台长时任务成对使用,前者展示进度,后者保活进程
  2. sequence 每次更新必须递增,否则系统忽略
  3. keepAlive 间隔必须小于 keepTime,留足余量
  4. 在 onForeground 和 onDestroy 中都处理关闭,避免残留
  5. startLiveView 的 1003500006 错误码要 catch 并改为 updateLiveView
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐