资讯详情 Live View Kit+ArkTS:排队实况窗双通道更新水位收敛【鸿蒙心迹】
📅 2026/10/11 22:43:03
排队取餐这种场景用户看到的状态看似很简单前面还有多少单、预计等待多久、是不是轮到自己。但在工程上真正麻烦的不是画一条进度条而是同一个排队事件可能由本地应用和服务端推送两条通道先后更新。两边网络和生命周期不同旧消息未必先到。若UI收到“前方还有12单”后又被一条延迟抵达的“前方还有15单”覆盖用户会认为队伍倒退了。因此本文不从实况窗模板样式入手而是从事件版本的所有权入手。Demo叫QueueLiveFence测试任务LV-1010-14、业务排队号Q-274、演示性实况窗ID41027。我们设定业务版本从41递增到45共收到7次更新5条有效版本被接纳1条重复版本43和1条迟到版本42被拒绝。当前水位45前方数量12应用状态HANDOFF_PENDING。平台本地创建、Push Kit更新与结束流程都标记NOT_RUN因为当前没有申请到业务服务能力也没有真实设备、Push Token或服务端接入证据。一、实况窗不等于应用里的悬浮窗先把一个容易混淆的术语讲清楚Live View Kit的“实况窗”是一种在通知中心、状态栏、锁屏等系统位置展示持续服务动态的能力它不是Window Kit创建的应用悬浮窗也不是本系列先前写过的FloatView。这两类能力在生命周期、操作权限和界面承载位置上有本质差异不能因为中文都带“窗”就套用相同的窗口API。华为官方实况窗指南列出了取餐、排队、配送、出行等持续性服务场景并提供liveViewManager.isLiveViewEnabled()、startLiveView、updateLiveView、stopLiveView等能力。官方同时指出本地更新依赖应用进程存活若希望进程离开后仍持续更新和结束推荐由Push Kit按业务事件更新。于是我们有两个状态体系业务订单的权威状态在后端设备端的实况窗只是其中一个可见的投影。应用不能把“调用显示接口返回”当成“业务订单已经完成”。这个区分直接影响本例做什么、不做什么。我们确实可以在本地构造一组有序和乱序消息并验证一个应用层的序号门禁会如何处理但无法在这里证明设备已经获得实况窗权限、服务端已经关联pushToken、用户没有主动清除通知、系统最终已经停止实况窗。第一件事是模型可测的后几件事都要有真实集成证据。本文不会把模型的ACCEPT写成系统级的UPDATED。二、只认单调递增的业务版本不认谁最后抵达先固定数据协议。业务事件包含queueId、version、aheadCount、state、occurredAt、eventId。其中version由同一队列号对应的业务源分配必须保证同一个队列里单调递增设备端自己使用Date.now()作为版本是错误方案因为两台设备时钟可能不同网络传输时间也无法代表业务变更顺序。在我们的事件序列里10:20接纳4110:21接纳4210:22接纳43随后又来一条43判定为重复10:23接纳44接着迟到的42再来一次只能丢弃10:24接纳45。5条有效事件代表“创建后的五个演示业务版本”并不等于真实系统SDK调用成功5次。最终水位45、eventsReceived7、applied5、duplicateIgnored1、staleIgnored1。此处aheadCount12只是最后一条演示业务数据。需要明确终态规则。排队事件最终可能进入SERVED或CANCELLED一旦写入终态任何没有更高权威版本的旧进度都不能把它重新改回排队中。更棘手的是同版本不同payload这不是“最后写入胜出”而应该记录协议冲突并发出服务端核查信号不能静默覆盖。若业务允许在取消后重新排队应该产生新的队列实例ID或新的业务epoch不能复活旧会话。为了让这个判断可以独立测试先写一段完全不依赖平台接口的ArkTS模型。QueueRevisionGate是应用自己定义的类ACCEPT、DUPLICATE、STALE也是本文私有状态它们不是Live View Kit返回的枚举值。interfaceQueueEvent{queueId:stringeventId:stringversion:numberaheadCount:numberstate:WAITING|SERVED|CANCELLED}typeVerdictACCEPT|DUPLICATE|STALE|CONFLICTclassQueueRevisionGate{privatewatermark:number40privateseen:Mapnumber,stringnewMap()publicapplied:number0publicduplicateIgnored:number0publicstaleIgnored:number0accept(event:QueueEvent):Verdict{if(event.versionthis.watermark){this.staleIgnored1returnSTALE}constoldIdthis.seen.get(event.version)if(oldId!undefined){if(oldId!event.eventId)returnCONFLICTthis.duplicateIgnored1returnDUPLICATE}if(event.versionthis.watermark){returnCONFLICT}this.watermarkevent.versionthis.seen.set(event.version,event.eventId)this.applied1returnACCEPT}version():number{returnthis.watermark}}这份代码不是最终完整的分布式去重实现。为了避免所有相同版本事件被错误地解释成业务冲突服务端应该提供稳定eventId并约定何时重试保留相同ID。当前代码在进程重启后会丢失seen因此如果系统真正接入我们还必须把会话ID、水位和终态持久化并与服务端恢复快照核对。示例优先展示“乱序不能倒灌”的条件判断不虚构它解决了全部可靠投递问题。1. 水位持久化才真正面对进程重启纯内存模型里的watermark45足够展示判定但用户把应用切后台、系统回收进程后内存数据会消失。再次打开页面时如果无条件从40开始就可能让缓存旧消息42重新进入ACCEPT让队伍人数出现倒退。真实接入应保存队列实例ID、事件epoch、业务水位及终态再在恢复阶段向可信服务核实最新权威版本。持久化是缩短恢复路径的线索不是代替服务端业务真相的账本。还要考虑排队编号被重复使用。两家门店在同一天都可能出现Q-274即便是同一家门店不同日期也可能重号。本文只用queueId作为教学示例主键正式业务还需加入商户、租户、业务日期或服务端签发的不可复用实例ID。不能把Push Token、设备身份或手机号写入公开HiLog来凑去重条件那既有安全风险也会把本不该进入系统通知的数据暴露出去。三、平台调用先过准入门不把模板构造当成投递结果只有通过了业务版本门禁才能考虑更新实况窗。但“考虑更新”仍有多重前置条件实况窗开关是否开启当前设备和应用是否符合场景接入要求是否已有可更新的实例实例是否被用户清除以及是否有可靠的推送交接记录。官方文档明确提醒本地创建与本地更新需要进程和应用场景条件Push Kit交接也要求服务端保存实况窗ID、pushToken、业务场景与状态属性不能在本地凭空造一个token。下面第二段代码只演示一层能力门禁保留平台正式调用点不用一套看起来能运行却缺少完整LiveView结构的伪代码冒充已编译项目。集成时应参考官方取餐/排队模板示例构造包含event、layoutData、clickAction等字段的liveViewManager.LiveView经能力开通、真机检验后再执行。本文不会把纯数字41027视为系统已经确认有效的实况窗ID。import{liveViewManager}fromkit.LiveViewKittypeDeliveryGateREADY_TO_CALL|DISABLED|NOT_PREPAREDclassLiveViewPort{privateprepared:booleanfalsemarkPrepared(yes:boolean):void{this.preparedyes}asynccanCall():PromiseDeliveryGate{if(!this.prepared)returnNOT_PREPAREDconstenabled:booleanawaitliveViewManager.isLiveViewEnabled()returnenabled?READY_TO_CALL:DISABLED}// 接入真实服务后再用官方LiveView完整结构调用// await liveViewManager.startLiveView(view)// await liveViewManager.updateLiveView(view)// await liveViewManager.stopLiveView(view)}这里preparedfalse是一种刻意选择。它让本轮截图中的localStartNOT_RUN、pushUpdateNOT_RUN保持真实含义模型没有走任何平台系统调用。这不是SDK故障更不是用户禁用服务的实测结论。开发联调时canCall()自身也可能抛出业务错误必须捕获并记录错误码及版本环境。若用户关闭了实况窗开关产品可以继续展示应用内订单页但不应冒充系统卡片仍在更新。四、把双通道交接写成业务状态机本地应用与Push Kit之间不能靠“两个模块都在写一份UI”协同。理想路径是服务端成为唯一真相源客户端创建实况窗成功并拿到确认后将ID与场景信息上报服务端关联pushToken之后每个业务事件持有服务端版本服务端判断要走Push更新、终止还是不应再发送。客户端可以在前台做本地展示和按官方规则尝试本地更新但它和Push都必须尊重同一业务版本与终态。我们把状态分为LOCAL_READY、START_REQUESTED、SERVER_BOUND、HANDOFF_PENDING、PUSH_ACTIVE、END_REQUESTED、ENDED另外保留USER_REMOVED和FAILED。这组名称属于应用状态模型并非系统API官方状态名。当前演示只有HANDOFF_PENDING五条模型事件已经经过序号筛选业务输出准备好但没有证据表明本地系统实例创建成功更谈不上服务端绑定完成。为了保持这个边界图片中的两个系统调用指标都写NOT_RUN。不要把服务端版本当成系统展示版本。推送接口返回成功可能仅表示服务接收请求不保证用户设备立刻显示用户也可能手动删除实况窗。用户删除后继续用相同ID反复尝试更新不一定符合平台行为必须参考Live View Kit的清除与恢复限制。应用的正确状态应同时包含businessRevision和deliveryAcknowledgement后者可能一直停留在待核实而前者已经到了45。1. 推送受理、设备到达与业务提交要有三本账把一次版本45更新拆成三个时刻会更清楚业务服务先将排队状态写入权威账本推送服务随后受理投递请求用户设备最后才可能接收并显示新内容。这三步相隔数秒甚至可能某一步没有回执。业务日志只能证明“version45 committed”服务端返回只能证明请求被受理设备真正观察到新内容必须靠真实系统回调或测试证据。若只记一个successtrue下一次用户投诉手机仍显示旧人数时根本无法判定出错链路。双通道还要规定失败优先级。前台本地更新超时不能立即推断后台推送也失败推送受理成功也不能倒推通知必然显示。任何展示回执与业务状态冲突时都应先保留业务权威版本把投影异常写入独立审计队列而非倒退业务人数迁就系统卡片。对取餐这种与到店时机相关的体验宁愿明确告诉用户“系统通知同步中”也不能给出没有来源的“已更新成功”。五、用一张主屏幕解释模型结果而不是伪造通知中心我们的QueueHomePage展示“取餐排队Q-274前方12单”并画出41、42、43、44、45的有效演示事件进程。下面的统计卡片写收到7条、应用5条、重复忽略1条、过期忽略1条当前业务版本45。HANDOFF_PENDING提醒观众这只是一组业务序列验证。旁边明确展示本地创建实况窗、推送更新都为NOT_RUN避免把内部业务屏误认成系统通知中心或锁屏实况窗截图。这一层产品文案非常重要。对用户而言“更新成功”往往意味着系统界面已经被刷新对开发者而言它可能只是“事件通过了前置门禁”。二者不能混用。如果正式应用拿不到系统确认就应显示“等待同步”或保留应用内状态而不是告诉用户“系统通知已更新”。同理业务端前方人数12只是排队服务返回的数据不代表系统UI展示可以承诺剩余等待时间18分钟一定准确。六、七条消息怎样验证四种相反结论为了复核门禁下面给出不需要后端的固定输入序列。Q-274的41、42、43、44、45五条有效消息分别拥有稳定eventId重复43再次到达但eventId不变迟到42携带已经被后续版本覆盖的旧版本。模型的输出应依次是ACCEPT, ACCEPT, ACCEPT, DUPLICATE, ACCEPT, STALE, ACCEPT。这里没有模拟业务终态HANDOFF_PENDING也不是SERVED不能让系统消息误关窗。第三段代码是一个可以直接放进ArkTS纯函数测试的最小夹具。通过明确的数组与断言既能避免图片中的日志凭空捏造也能给后续服务端接入留出回归入口。constgatenewQueueRevisionGate()constrows:QueueEvent[][{queueId:Q-274,eventId:e41,version:41,aheadCount:19,state:WAITING},{queueId:Q-274,eventId:e42,version:42,aheadCount:17,state:WAITING},{queueId:Q-274,eventId:e43,version:43,aheadCount:16,state:WAITING},{queueId:Q-274,eventId:e43,version:43,aheadCount:16,state:WAITING},{queueId:Q-274,eventId:e44,version:44,aheadCount:14,state:WAITING},{queueId:Q-274,eventId:e42,version:42,aheadCount:17,state:WAITING},{queueId:Q-274,eventId:e45,version:45,aheadCount:12,state:WAITING}]constactual:Verdict[]rows.map((row:QueueEvent)gate.accept(row))constexpected:Verdict[][ACCEPT,ACCEPT,ACCEPT,DUPLICATE,ACCEPT,STALE,ACCEPT]constpassedJSON.stringify(actual)JSON.stringify(expected)gate.version()45gate.applied5gate.duplicateIgnored1gate.staleIgnored1为什么这里不顺手接上真实updateLiveView因为测试尚未具有完整业务准入、WantAgent配置和服务端存储凭据。纯函数测试通过只能证明“如果收到这些输入应用层会做这些判定”。这也是工程报告里必须写清的适用边界。正式环境还要加入消息签名或可信来源鉴别拒绝其他queueId混入处理重连后的水位恢复并为终态冲突定义人工复核和日志留存规则。1. 终态允许重试但不能重复产生业务副作用如果后续收到SERVED事件例如版本46宣告取餐完成不能把“发出了结束请求”直接视作最终终态。网络超时可能导致多次相同终止请求服务端应以队列实例与终态版本为幂等键。客户端收到同版本终态重放时不应再创建新实况窗更不能重置计时器。系统结束接口失败时必须保留待核实状态及有界重试窗口用户已经清除了实况窗时则应尊重平台行为不可无限重建同一个ID。还可以把“水位应该前进”和“是否值得通知用户”分开。版本46可能只是更新内部审计属性前方人数仍是12这仍然是有效业务版本却不一定值得额外调用一次updateLiveView。业务门禁负责接纳权威版本展示策略负责判断可见内容是否变化这样既减少无意义系统更新又保留完整审计链。不能为了节流而丢掉权威版本事件更不应把系统限流简单理解成业务允许倒退。七、实况窗时间窗口、用户清除与终态比UI更重要官方对实况窗生命周期和显示行为有明确限制。实况窗存在最长生命周期更新长期停滞会影响状态栏、锁屏或通知中心的展示用户还可以主动清除。系统行为与业务队列事件不是一回事。应用要保留业务状态真相同时正确停止已不符合展示条件的实况窗。排队号已经被服务端取消不能只因为本地还显示12单就一直让系统窗口驻留。尤其注意stopLiveView和业务“已服务”不是同一个动作。理想路径是先由权威业务终态产生更高版本经应用或服务器确认需要结束窗口再按官方接口更新/停止并记录结果。如果进程已经退出本地方式不能保证按时结束必须依赖满足条件的推送通道。当前模型并未接入Push Kit因此终态收敛只能写成待实现的工程设计不能把stopLiveView在注释里出现当作已真正执行。另一方面也不能把Push Kit的频率限制理解成应用可以无限补发。官方文档对不同场景给出更新频率、持续时间及网络图片等限制正式系统要优先合并短时间内频繁抖动的业务人数再发送对用户有意义的更新。若排队人数在数秒内来回变化系统通知每次都刷可能造成干扰。应用可按业务稳定窗口合并消息但不允许用合并结果倒退业务序号。平台限流和业务降噪应分开记录否则排查时很难知道到底是被服务丢弃还是主动没有发送。诊断图如果出现updateLiveViewContent之类文字应理解为演示界面自行定义的动作说明不是华为SDK方法名官方公开的更新入口以liveViewManager.updateLiveView为准。诊断页中逐条保留版本和判定43重复、42过期两个被拒的事件有不同原因它们加起来等于2但不能合并成一个模糊的“错误2次”。业务水位45与应用待交接状态可以同时为真。上报日志时还应保留LiveView请求是否真正调用、result返回值、Push服务受理ID及终态通知任何缺失都必须保持NOT_VERIFIED而非随意补零。1. 系统级展示的隐私与可访问性也要验收实况窗进入状态栏、锁屏后内容可能被旁观者看到。排队号、店名及剩余人数通常更容易设计为低敏业务提示但顾客姓名、电话号码、订单备注等不应因为原始接口里存在就自动塞进系统卡片。应建立明确的字段白名单并分别核对胶囊态、卡片态和小折叠外屏可显示的信息避免把移动应用详情页的个人信息不加筛选复制过去。模板能承载某字段不代表业务必须展示它。字体放大、语音读屏和多语言同样会影响体验。一位数人数变成两位数时高亮数字不应挤占主要内容颜色不能成为判断“已叫号”的唯一依据。真实验收要覆盖从系统通知点击后回到正确排队详情的路径并检查用户在后台、锁屏及不同设备中收到更新的差异。当前演示没有真正的WantAgent、Push Token和系统展示截图因此上述项目只能列入待验收清单不能写“通过”或虚构测试耗时。八、可上线前必须补上的真实验证本轮能作为证据的是已定义的输入向量、模型输出规则和可读UI示意收到7、接纳5、拒绝重复1、拒绝过期1、业务版本45、前方12单。它们说明门禁的业务意图不能证明任何一部HarmonyOS设备已创建系统实况窗也不能证明后台推送已经成功。集成阶段至少需要在已开通Live View Kit的开发者应用中检查用户开关、模板字段、WantAgent跳转、Push Token上报、服务端绑定、冷后台更新和窗口结束回执。还有几个不容易提前发现的边界用户主动删除后继续更新、应用重装后旧业务队列ID复用、服务端连续发出重复终态、用户时区变化导致显示时间解释差异以及同一账号在多设备上同时持有不同实况窗ID。每一项都要求独立场景记录。单设备里的JavaScript或ArkTS断言即使全部通过也不能替代真实消息在网络和系统通道中的传输验证。对课程或技术文章而言最有价值的结论不是“几行代码就做出了实况窗”而是把交付边界摆正本地业务版本水位负责阻止旧状态倒灌平台实况窗负责展示、推送负责跨进程更新业务终态负责真正结束服务。三者各自拥有可验证的凭据和失败状态工程才有可能稳定收尾。如果把它们都塞进一个布尔值isLiveViewSuccess任何随机迟到消息都足以让原本正常的排队体验失去可信度。文档与能力边界QUEUE场景、Live View Kit相关API以及本地/Push交接方向来自华为官方指南。QueueRevisionGate、HANDOFF_PENDING和全部任务数字为应用自定义演示模型本轮未执行平台调用。参考链接https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-v5/liveview-create-locally-V5https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/liveview-update-by-pushhttps://developer.huawei.com/consumer/en/doc/harmonyos-guides-V14/liveview-introduction-V14https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/liveview-faq-3-V5