HarmonyOS 7 新特性(四十五)|实况窗扩展:锁屏、卡片与生命周期
API 26 Release 对 Live View Kit 的导出符号进行了明确调整:除 liveViewManager 和 CardInfo 外,还导出了锁屏扩展与卡片扩展相关的 Ability、Context。这个变化看似只是 d.ts 导出列表,实际上提醒开发者:实况窗不是一条“发通知”接口,而是由应用进程、扩展生命周期、系统展示和服务端 Push 共同维护的长期任务。
本文聚焦第十八篇未展开的方向:如何围绕 LiveViewLockScreenExtensionAbility、LiveViewCardExtensionAbility 等扩展入口,设计生命周期、状态持久化、幂等更新、终态回收和 Release 迁移。

一、先理解四类运行主体
一个实况窗至少涉及业务服务端、应用主进程、系统 Live View 服务和扩展 Ability。它们不会永远同时存活。
interface LiveViewOwnership {
businessServer: 'AUTHORITATIVE_STATE'
appProcess: 'CREATE_AND_INTERACT'
extension: 'RENDER_AND_HANDLE_SYSTEM_ENTRY'
system: 'DISPLAY_AND_SCHEDULE'
}
业务状态的单一事实源通常在服务端。扩展不能只读取主页面内存,否则主进程回收后就失去内容。
二、Release 导出变化要显式迁移
不要继续依赖内部路径或隐式全局符号。统一从 Kit 的公开导出入口引用,并固定 SDK 版本。
import {
liveViewManager,
LiveViewLockScreenExtensionAbility,
LiveViewLockScreenExtensionContext,
LiveViewCardExtensionAbility,
LiveViewCardExtensionContext,
CardInfo
} from '@kit.LiveViewKit'
上面名称来自 API 26 Release 差异文档;具体可继承方法、回调参数和设备支持范围仍应以目标 SDK 的类型声明为准。
三、用稳定 ID 连接四端状态
每项活动需要业务 ID、实况窗 ID、用户范围和状态版本。数据库不要只保存“是否创建”。
interface LiveActivityRecord {
activityId: string
liveViewId: string
accountHash: string
event: string
stateVersion: number
status: 'CREATING' | 'ACTIVE' | 'ENDING' | 'ENDED'
updatedAt: number
}
activityId 由业务生成且幂等;liveViewId 用于调用 Live View;stateVersion 防止乱序更新。
四、创建采用两阶段提交
创建实况窗成功但本地未保存映射,或者本地写入成功但系统创建失败,都会留下孤儿。使用“准备—创建—确认”的状态迁移。
async function createActivity(input: CreateInput) {
const record = await store.prepare(input.activityId)
if (record.status === 'ACTIVE') return record
const result = await liveViewGateway.create(input)
return store.confirmCreated(input.activityId, result.liveViewId)
}
重试先按业务 ID 查询已有记录,不重复创建多个实况窗。

五、扩展启动时只做快速恢复
系统可能在锁屏或卡片入口单独拉起扩展。回调中避免执行重型初始化、长网络请求和大图片解码。
interface ExtensionSnapshot {
activityId: string
version: number
title: string
subtitle: string
progress?: number
terminal: boolean
}
async function restoreForExtension(id: string): Promise<ExtensionSnapshot> {
return snapshotStore.read(id) ?? fallbackSnapshot(id)
}
先用本地最小快照渲染,再通过系统支持的更新通道刷新。快照不保存令牌和完整订单敏感信息。
六、锁屏与卡片共享状态、分离表现
不同展示面空间、交互和隐私要求不同。共享业务状态模型,但由各自的 Presenter 生成展示数据。
interface ActivityState {
phase: string
etaMinutes?: number
progress?: number
sensitiveDetail?: string
}
function toLockScreen(s: ActivityState): CardInfoModel {
return { title: s.phase, detail: `${s.etaMinutes ?? '--'} 分钟` }
}
function toCard(s: ActivityState): CardInfoModel {
return { title: s.phase, detail: '点击查看详情' }
}
锁屏默认隐藏姓名、地址、手机号和订单金额等敏感内容。
七、所有更新必须版本化和幂等
移动网络会让消息重试、延迟、重复和乱序。新状态只能覆盖旧状态,终态不能被运行态复活。
function acceptUpdate(current: LiveActivityRecord, incomingVersion: number,
incomingStatus: LiveActivityRecord['status']): boolean {
if (current.status === 'ENDED') return false
if (incomingVersion <= current.stateVersion) return false
if (current.status === 'ENDING' && incomingStatus === 'ACTIVE') return false
return true
}
客户端和服务端使用同一版本语义,并记录拒绝原因。
八、Push 更新与扩展恢复协同
官方建议在应用进程结束后通过 Push Kit 更新实况窗。服务端保存 liveViewId、Push Token、event 和业务状态;扩展读取本地快照是兜底,不应与 Push 各自维护两套状态机。
interface PushEnvelope {
activityId: string
version: number
event: 'UPDATE' | 'END'
payload: Record<string, unknown>
sentAt: number
}
收到 Push 后先验证活动、账号、版本和时效,再更新系统展示与快照。
九、终态必须一次完成
结束可能由服务端、用户取消、超时或应用恢复触发。所有入口进入统一 endOnce。
async function endOnce(activityId: string, reason: string) {
const record = await store.beginEnding(activityId)
if (!record || record.status === 'ENDED') return
await liveViewGateway.end(record.liveViewId, reason)
await store.markEnded(activityId)
await snapshotStore.remove(activityId)
}
如果系统调用失败,保留 ENDING 以便重试;不要提前删除唯一映射。
十、深链交互要重新鉴权
用户从锁屏或卡片点击进入应用时,传递的只能是稳定业务标识和动作类型。应用恢复后重新确认账号、权限和数据状态。
interface LiveViewIntent {
activityId: string
action: 'OPEN_DETAIL' | 'CANCEL' | 'CONTACT'
}
async function handleIntent(intent: LiveViewIntent) {
await auth.requireUnlockedSession()
const latest = await activityApi.get(intent.activityId)
return router.open(resolveDestination(latest, intent.action))
}
锁屏入口不能绕过敏感操作确认。
十一、资源与超时治理
实况窗只适合具有时段性、时效性和变化性的任务。创建后长时间不更新、终态不结束,会占用系统展示资源并伤害用户信任。
interface ActivityDeadline {
expectedEndAt: number
hardExpireAt: number
staleAfterMs: number
}
function needsReconcile(d: ActivityDeadline, now: number, updatedAt: number) {
return now > d.hardExpireAt || now - updatedAt > d.staleAfterMs
}
应用启动、账号切换和定时任务时执行对账,清理孤儿记录并结束过期活动。
十二、生命周期测试矩阵
const lifecycleCases = [
'create-then-kill-app',
'push-update-while-killed',
'open-from-lock-screen',
'duplicate-update',
'out-of-order-update',
'account-switch',
'expire-without-network',
'end-retry-after-failure'
]
每个用例验证系统展示、本地记录、服务端状态和扩展快照四处一致。真机测试锁屏、通知中心、状态栏和熄屏等实际形态。

十三、上线检查清单
- 已迁移到 API 26 Release 的公开导出符号;
- 主进程、扩展、系统和服务端职责明确;
- 活动 ID、实况窗 ID、账号与版本映射可追踪;
- 创建与结束操作幂等;
- 扩展启动不依赖主进程内存;
- 锁屏默认隐藏敏感信息;
- Push 与本地快照使用同一状态版本;
- 乱序、重复和终态回退被拒绝;
- 深链进入后重新鉴权;
- 过期、孤儿和失败结束有对账任务。
结语
API 26 Release 的导出调整让锁屏扩展和卡片扩展的角色更清晰,也把实况窗的工程难点暴露出来:真正需要管理的是跨进程、跨入口、跨网络的长期状态。以服务端为事实源,以版本化快照支撑扩展恢复,再用幂等创建、更新和结束守住生命周期,实况窗才能在应用被回收之后仍保持准确。
官方参考
- Live View Kit API 26 Release 差异:https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/js-apidiff-liveviewkit-7003
- Live View Kit 产品介绍:https://developer.huawei.com/consumer/cn/sdk/live-view-kit
- 通过 Push Kit 更新实况窗:https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/liveview-update-by-push
更多推荐



所有评论(0)