一、背景说明
实况窗(Live View)是鸿蒙系统中一种聚焦正在进行的任务的通知形态,具有时效性、时段性、变化性的特点,典型场景包括即时配送、打车出行、航旅值机等。在最新的 HarmonyOS 7.0.0.111 版本中,系统进一步开放了实况窗的分享能力(通过 LiveView 结构体的shareUrl字段),实况窗开发正成为鸿蒙应用的标准配置。
本文以即时配送场景为例,基于 Live View Kit 的liveViewManager模块,完整实现实况窗的创建、更新与结束全生命周期流程。文中代码基于 HarmonyOS 6.0.0 Release SDK 及以上版本(API 12+),DevEco Studio 6.0.0 Release 及以上。
二、技术原理
实况窗由liveViewManager模块提供基础能力,核心接口共四个:
isLiveViewEnabled():校验实况窗开关(设置 > 应用和元服务 > 应用名 > 实况窗)是否打开,这是创建实况窗的前提;startLiveView(liveView):创建实况窗,传入LiveView实例;updateLiveView(liveView):更新实况窗内容;stopLiveView(liveView):结束实况窗生命周期。
LiveView结构体中,event声明应用场景(如DELIVERY即时配送、TAXI打车、TRAIN高铁等),liveViewData.primary承载主区内容,layoutData控制扩展区模板——共 5 种样式模板:进度可视化模板(LAYOUT_TYPE_PROGRESS)、强调文本模板、左右文本模板、赛事比分模板和导航模板。
本地实况窗依赖应用进程存活。若应用需要退到后台后仍持续更新实况窗,需配合 Push Kit 云侧 REST API(operation值 0/1/2 分别对应创建/更新/结束)。
三、实现步骤
- 开通权益
实况窗属于受限开放能力,需先在 AppGallery Connect 中开通推送服务权益与实况窗权益,并将AppScope/app.json5中的bundleName替换为自己已开通权益的包名。
- 工程配置
在entry/src/main/module.json5中添加网络权限:
{ "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET" } ] } }- 构建上下文工具类
实况窗的clickAction字段需要 WantAgent 对象,实现点击实况窗拉起应用指定页面。封装工具类ContextUtil.ets:
// utils/ContextUtil.etsimport{wantAgent,WantAgent}from'@kit.AbilityKit';import{hilog}from'@kit.PerformanceAnalysisKit';constDOMAIN:number=0x0000;constTAG:string='ContextUtil';exportclassContextUtil{privatestaticwantAgentObj:WantAgent|null=null;/** * 构建 WantAgent,用于实况窗点击后跳转到应用首页 * abilityName 参数可在资源表中配置,此处以 EntryAbility 为例 */publicstaticasyncbuildWantAgent(abilityName:string):Promise<WantAgent|undefined>{try{constwantAgentInfo:wantAgent.WantAgentInfo={wants:[{bundleName:'com.example.liveviewdemo',abilityName:abilityName}],actionType:wantAgent.OperationType.START_ABILITY,requestCode:0,actionFlags:[wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]};this.wantAgentObj=awaitwantAgent.getWantAgent(wantAgentInfo);returnthis.wantAgentObj;}catch(err){hilog.error(DOMAIN,TAG,'buildWantAgent error: %{public}d %{public}s',(errasBusinessError).code,(errasBusinessError).message);returnundefined;}}}注意:需要在文件头部补充import { BusinessError } from '@kit.BasicServicesKit';。
- 实况窗控制类
封装实况窗全生命周期操作DeliveryLiveViewUtil.ets:
// utils/DeliveryLiveViewUtil.etsimport{liveViewManager}from'@kit.LiveViewKit';import{BusinessError}from'@kit.BasicServicesKit';import{hilog}from'@kit.PerformanceAnalysisKit';import{ContextUtil}from'./ContextUtil';constDOMAIN:number=0x0000;constTAG:string='DeliveryLiveView';exportclassDeliveryLiveViewUtil{privatestaticviewId:number=106;// 校验实况窗开关publicstaticasyncisLiveViewEnabled():Promise<boolean>{try{returnawaitliveViewManager.isLiveViewEnabled();}catch(err){conste=errasBusinessError;hilog.error(DOMAIN,TAG,'isLiveViewEnabled error: %{public}d %{public}s',e.code,e.message);returnfalse;}}// 创建实况窗:骑手已接单(进度可视化模板)publicstaticasyncstartLiveView():Promise<void>{if(!awaitthis.isLiveViewEnabled()){hilog.warn(DOMAIN,TAG,'live view is disabled.');return;}try{constliveView:liveViewManager.LiveView={id:this.viewId,event:'DELIVERY',isMute:false,liveViewData:{primary:{title:'骑手已接单',content:[{text:'距商家 '},{text:'300 ',textColor:'#FF0A59F7'},{text:'米 | '},{text:'3 ',textColor:'#FF0A59F7'},{text:'分钟到店'}],keepTime:0,clickAction:awaitContextUtil.buildWantAgent('EntryAbility'),layoutData:{layoutType:liveViewManager.LayoutType.LAYOUT_TYPE_PROGRESS,progress:40,color:'#FF317AF7',backgroundColor:'#f7819ae0',indicatorType:liveViewManager.IndicatorType.INDICATOR_TYPE_UP,indicatorIcon:'icon_rider.png',lineType:liveViewManager.LineType.LINE_TYPE_DOTTED_LINE,nodeIcons:['icon_order.png','icon_store_white.png','icon_finish.png']}}}};constresult=awaitliveViewManager.startLiveView(liveView);hilog.info(DOMAIN,TAG,'startLiveView result: %{public}s',JSON.stringify(result));}catch(err){conste=errasBusinessError;hilog.error(DOMAIN,TAG,'startLiveView error: %{public}d %{public}s',e.code,e.message);}}// 更新实况窗:商品配送中(progress 推进到 70)publicstaticasyncupdateLiveView():Promise<void>{try{constliveView:liveViewManager.LiveView={id:this.viewId,event:'DELIVERY',isMute:false,liveViewData:{primary:{title:'商品配送中',content:[{text:'预计 '},{text:'10 ',textColor:'#FF0A59F7'},{text:'分钟后送达'}],keepTime:0,clickAction:awaitContextUtil.buildWantAgent('EntryAbility'),layoutData:{layoutType:liveViewManager.LayoutType.LAYOUT_TYPE_PROGRESS,progress:70,color:'#FF317AF7',backgroundColor:'#f7819ae0',indicatorType:liveViewManager.IndicatorType.INDICATOR_TYPE_UP,indicatorIcon:'icon_rider.png',lineType:liveViewManager.LineType.LINE_TYPE_DOTTED_LINE,nodeIcons:['icon_order.png','icon_store_white.png','icon_finish.png']}}}};constresult=awaitliveViewManager.updateLiveView(liveView);hilog.info(DOMAIN,TAG,'updateLiveView result: %{public}s',JSON.stringify(result));}catch(err){conste=errasBusinessError;hilog.error(DOMAIN,TAG,'updateLiveView error: %{public}d %{public}s',e.code,e.message);}}// 结束实况窗:商品已送达publicstaticasyncstopLiveView():Promise<void>{try{constliveView:liveViewManager.LiveView={id:this.viewId,event:'DELIVERY',isMute:false,liveViewData:{primary:{title:'商品已送达',content:[{text:'感谢您的订单,欢迎再次下单'}],keepTime:15}}};constresult=awaitliveViewManager.stopLiveView(liveView);hilog.info(DOMAIN,TAG,'stopLiveView result: %{public}s',JSON.stringify(result));}catch(err){conste=errasBusinessError;hilog.error(DOMAIN,TAG,'stopLiveView error: %{public}d %{public}s',e.code,e.message);}}}- 页面调用
在首页放置三个按钮,分别触发创建、更新、结束:
// pages/Index.etsimport{DeliveryLiveViewUtil}from'../utils/DeliveryLiveViewUtil';@Entry@Componentstruct Index{build(){Column({space:20}){Text('实况窗 Demo:即时配送场景').fontSize(20).fontWeight(FontWeight.Bold)Button('提交订单(创建实况窗)').width('60%').onClick(()=>{DeliveryLiveViewUtil.startLiveView();})Button('更新进展(更新实况窗)').width('60%').onClick(()=>{DeliveryLiveViewUtil.updateLiveView();})Button('确认收货(结束实况窗)').width('60%').onClick(()=>{DeliveryLiveViewUtil.stopLiveView();})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}}- 资源文件
在entry/src/main/resources/rawfile/目录下放置进度条指示器图标icon_rider.png与三个节点图标icon_order.png、icon_store_white.png、icon_finish.png。
四、运行效果说明
真机运行后:
- 点击「提交订单」,状态栏与锁屏出现实况窗卡片,显示"骑手已接单",扩展区为进度条(40%),节点图标依次为订单-商家-完成;
- 点击「更新进展」,卡片标题变为"商品配送中",进度推进到 70%;
- 点击「确认收货」,实况窗进入结束态并按
keepTime: 15留存 15 秒后消失; - 结束后 15 秒留存期内不可重复创建同一 id 的实况窗。
五、技术要点
开关校验必须前置:用户未开启实况窗开关时调用
startLiveView会返回错误码 1001(权限未开通)或直接创建失败,创建前统一调用isLiveViewEnabled()可以规避大部分异常。keepTime 的两种语义:创建/更新时
keepTime: 0表示本次内容立即展示;结束实况窗时keepTime: 15表示结束态卡片留存 15 秒再消失,给用户一个明确的"已完成"反馈。textColor 的一致性约束:
content数组中所有设置textColor的对象仅能使用同一种颜色,混用多种颜色时以第一个为准,未设置时默认#FF000000。后台更新必须走 Push Kit:本地
updateLiveView依赖应用进程,应用被冻结或退出后实况窗将停留在最后一次状态。生产环境推荐"本地创建 + 云侧 Push Kit 更新"的组合,Push Kit REST API 的operation字段 0/1/2 与端侧三个接口一一对应,liveView.id对应云侧activityId。错误处理:所有接口调用都应捕获
BusinessError,常见的错误码:1001 权益未开通、1600004 用户未开启实况窗开关、1600001 内部错误。可在错误回调中引导用户去设置页打开开关。
六、扩展建议
- 对接分享能力:HarmonyOS 7.0.0.111 起实况窗支持分享,在
LiveView中配置shareUrl字段即可让用户将订单进度分享给他人,适合电商协作收货、多人拼单等场景:
constliveView:liveViewManager.LiveView={id:106,event:'DELIVERY',shareUrl:'https://example.com/order/12345',// ...其余字段同上};计时器模板:取餐、运动锻炼等场景可使用
timer字段(isCountdown、isPaused)实现卡片上的正/倒计时展示,无需应用轮询刷新。胶囊形态:
capsuleType字段可让实况窗以胶囊形态常驻(如CAPSULE_TYPE_TEXT显示"待取餐"),配合extensionData自定义辅助区内容。
结语
本文基于 Live View Kit 的liveViewManager模块,用约 150 行代码实现了即时配送场景实况窗的完整生命周期(创建、更新、结束),并给出了权益开通、权限配置、WantAgent 跳转与错误处理的生产级细节。实况窗在 HarmonyOS 7 时代已从外卖打车的"专属能力"扩展到计时、取餐、运动等十类场景,且分享能力进一步打开使用面,建议开发者按业务场景尽早接入。需注意接口默认 QPS 有限制,高频更新建议合并且走 Push Kit 云侧链路。